Merge "benchmark: Add a build target that's not Android-dependent"
diff --git a/Android.mk b/Android.mk
index 0567b22..2647268 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,8 +82,6 @@
 include $(art_path)/tools/dexfuzz/Android.mk
 include $(art_path)/libart_fake/Android.mk
 
-
-# ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES
 ART_HOST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
   $(ART_HOST_DEX_DEPENDENCIES) \
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index 050100f..e784508 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -17,7 +17,7 @@
 art_cc_library {
     name: "libartbenchmark",
     host_supported: true,
-    defaults: ["art_defaults", "art_debug_defaults"],
+    defaults: ["art_defaults" ],
     srcs: [
         "jni_loader.cc",
         "jobject-benchmark/jobject_benchmark.cc",
@@ -28,6 +28,7 @@
     shared_libs: [
         "libart",
         "libbacktrace",
+        "libbase",
         "libnativehelper",
     ],
     clang: true,
diff --git a/benchmark/const-string/info.txt b/benchmark/const-string/info.txt
new file mode 100644
index 0000000..78f39d2
--- /dev/null
+++ b/benchmark/const-string/info.txt
@@ -0,0 +1 @@
+Benchmarks for repeating const-string instructions in a loop.
diff --git a/benchmark/const-string/src/ConstStringBenchmark.java b/benchmark/const-string/src/ConstStringBenchmark.java
new file mode 100644
index 0000000..2beb0a4
--- /dev/null
+++ b/benchmark/const-string/src/ConstStringBenchmark.java
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ConstStringBenchmark {
+    // Initialize 1025 strings with consecutive string indexes in the dex file.
+    // The tests below rely on the knowledge that ART uses the low 10 bits
+    // of the string index as the hash into DexCache strings array.
+    public static final String string_0000 = "TestString_0000";
+    public static final String string_0001 = "TestString_0001";
+    public static final String string_0002 = "TestString_0002";
+    public static final String string_0003 = "TestString_0003";
+    public static final String string_0004 = "TestString_0004";
+    public static final String string_0005 = "TestString_0005";
+    public static final String string_0006 = "TestString_0006";
+    public static final String string_0007 = "TestString_0007";
+    public static final String string_0008 = "TestString_0008";
+    public static final String string_0009 = "TestString_0009";
+    public static final String string_0010 = "TestString_0010";
+    public static final String string_0011 = "TestString_0011";
+    public static final String string_0012 = "TestString_0012";
+    public static final String string_0013 = "TestString_0013";
+    public static final String string_0014 = "TestString_0014";
+    public static final String string_0015 = "TestString_0015";
+    public static final String string_0016 = "TestString_0016";
+    public static final String string_0017 = "TestString_0017";
+    public static final String string_0018 = "TestString_0018";
+    public static final String string_0019 = "TestString_0019";
+    public static final String string_0020 = "TestString_0020";
+    public static final String string_0021 = "TestString_0021";
+    public static final String string_0022 = "TestString_0022";
+    public static final String string_0023 = "TestString_0023";
+    public static final String string_0024 = "TestString_0024";
+    public static final String string_0025 = "TestString_0025";
+    public static final String string_0026 = "TestString_0026";
+    public static final String string_0027 = "TestString_0027";
+    public static final String string_0028 = "TestString_0028";
+    public static final String string_0029 = "TestString_0029";
+    public static final String string_0030 = "TestString_0030";
+    public static final String string_0031 = "TestString_0031";
+    public static final String string_0032 = "TestString_0032";
+    public static final String string_0033 = "TestString_0033";
+    public static final String string_0034 = "TestString_0034";
+    public static final String string_0035 = "TestString_0035";
+    public static final String string_0036 = "TestString_0036";
+    public static final String string_0037 = "TestString_0037";
+    public static final String string_0038 = "TestString_0038";
+    public static final String string_0039 = "TestString_0039";
+    public static final String string_0040 = "TestString_0040";
+    public static final String string_0041 = "TestString_0041";
+    public static final String string_0042 = "TestString_0042";
+    public static final String string_0043 = "TestString_0043";
+    public static final String string_0044 = "TestString_0044";
+    public static final String string_0045 = "TestString_0045";
+    public static final String string_0046 = "TestString_0046";
+    public static final String string_0047 = "TestString_0047";
+    public static final String string_0048 = "TestString_0048";
+    public static final String string_0049 = "TestString_0049";
+    public static final String string_0050 = "TestString_0050";
+    public static final String string_0051 = "TestString_0051";
+    public static final String string_0052 = "TestString_0052";
+    public static final String string_0053 = "TestString_0053";
+    public static final String string_0054 = "TestString_0054";
+    public static final String string_0055 = "TestString_0055";
+    public static final String string_0056 = "TestString_0056";
+    public static final String string_0057 = "TestString_0057";
+    public static final String string_0058 = "TestString_0058";
+    public static final String string_0059 = "TestString_0059";
+    public static final String string_0060 = "TestString_0060";
+    public static final String string_0061 = "TestString_0061";
+    public static final String string_0062 = "TestString_0062";
+    public static final String string_0063 = "TestString_0063";
+    public static final String string_0064 = "TestString_0064";
+    public static final String string_0065 = "TestString_0065";
+    public static final String string_0066 = "TestString_0066";
+    public static final String string_0067 = "TestString_0067";
+    public static final String string_0068 = "TestString_0068";
+    public static final String string_0069 = "TestString_0069";
+    public static final String string_0070 = "TestString_0070";
+    public static final String string_0071 = "TestString_0071";
+    public static final String string_0072 = "TestString_0072";
+    public static final String string_0073 = "TestString_0073";
+    public static final String string_0074 = "TestString_0074";
+    public static final String string_0075 = "TestString_0075";
+    public static final String string_0076 = "TestString_0076";
+    public static final String string_0077 = "TestString_0077";
+    public static final String string_0078 = "TestString_0078";
+    public static final String string_0079 = "TestString_0079";
+    public static final String string_0080 = "TestString_0080";
+    public static final String string_0081 = "TestString_0081";
+    public static final String string_0082 = "TestString_0082";
+    public static final String string_0083 = "TestString_0083";
+    public static final String string_0084 = "TestString_0084";
+    public static final String string_0085 = "TestString_0085";
+    public static final String string_0086 = "TestString_0086";
+    public static final String string_0087 = "TestString_0087";
+    public static final String string_0088 = "TestString_0088";
+    public static final String string_0089 = "TestString_0089";
+    public static final String string_0090 = "TestString_0090";
+    public static final String string_0091 = "TestString_0091";
+    public static final String string_0092 = "TestString_0092";
+    public static final String string_0093 = "TestString_0093";
+    public static final String string_0094 = "TestString_0094";
+    public static final String string_0095 = "TestString_0095";
+    public static final String string_0096 = "TestString_0096";
+    public static final String string_0097 = "TestString_0097";
+    public static final String string_0098 = "TestString_0098";
+    public static final String string_0099 = "TestString_0099";
+    public static final String string_0100 = "TestString_0100";
+    public static final String string_0101 = "TestString_0101";
+    public static final String string_0102 = "TestString_0102";
+    public static final String string_0103 = "TestString_0103";
+    public static final String string_0104 = "TestString_0104";
+    public static final String string_0105 = "TestString_0105";
+    public static final String string_0106 = "TestString_0106";
+    public static final String string_0107 = "TestString_0107";
+    public static final String string_0108 = "TestString_0108";
+    public static final String string_0109 = "TestString_0109";
+    public static final String string_0110 = "TestString_0110";
+    public static final String string_0111 = "TestString_0111";
+    public static final String string_0112 = "TestString_0112";
+    public static final String string_0113 = "TestString_0113";
+    public static final String string_0114 = "TestString_0114";
+    public static final String string_0115 = "TestString_0115";
+    public static final String string_0116 = "TestString_0116";
+    public static final String string_0117 = "TestString_0117";
+    public static final String string_0118 = "TestString_0118";
+    public static final String string_0119 = "TestString_0119";
+    public static final String string_0120 = "TestString_0120";
+    public static final String string_0121 = "TestString_0121";
+    public static final String string_0122 = "TestString_0122";
+    public static final String string_0123 = "TestString_0123";
+    public static final String string_0124 = "TestString_0124";
+    public static final String string_0125 = "TestString_0125";
+    public static final String string_0126 = "TestString_0126";
+    public static final String string_0127 = "TestString_0127";
+    public static final String string_0128 = "TestString_0128";
+    public static final String string_0129 = "TestString_0129";
+    public static final String string_0130 = "TestString_0130";
+    public static final String string_0131 = "TestString_0131";
+    public static final String string_0132 = "TestString_0132";
+    public static final String string_0133 = "TestString_0133";
+    public static final String string_0134 = "TestString_0134";
+    public static final String string_0135 = "TestString_0135";
+    public static final String string_0136 = "TestString_0136";
+    public static final String string_0137 = "TestString_0137";
+    public static final String string_0138 = "TestString_0138";
+    public static final String string_0139 = "TestString_0139";
+    public static final String string_0140 = "TestString_0140";
+    public static final String string_0141 = "TestString_0141";
+    public static final String string_0142 = "TestString_0142";
+    public static final String string_0143 = "TestString_0143";
+    public static final String string_0144 = "TestString_0144";
+    public static final String string_0145 = "TestString_0145";
+    public static final String string_0146 = "TestString_0146";
+    public static final String string_0147 = "TestString_0147";
+    public static final String string_0148 = "TestString_0148";
+    public static final String string_0149 = "TestString_0149";
+    public static final String string_0150 = "TestString_0150";
+    public static final String string_0151 = "TestString_0151";
+    public static final String string_0152 = "TestString_0152";
+    public static final String string_0153 = "TestString_0153";
+    public static final String string_0154 = "TestString_0154";
+    public static final String string_0155 = "TestString_0155";
+    public static final String string_0156 = "TestString_0156";
+    public static final String string_0157 = "TestString_0157";
+    public static final String string_0158 = "TestString_0158";
+    public static final String string_0159 = "TestString_0159";
+    public static final String string_0160 = "TestString_0160";
+    public static final String string_0161 = "TestString_0161";
+    public static final String string_0162 = "TestString_0162";
+    public static final String string_0163 = "TestString_0163";
+    public static final String string_0164 = "TestString_0164";
+    public static final String string_0165 = "TestString_0165";
+    public static final String string_0166 = "TestString_0166";
+    public static final String string_0167 = "TestString_0167";
+    public static final String string_0168 = "TestString_0168";
+    public static final String string_0169 = "TestString_0169";
+    public static final String string_0170 = "TestString_0170";
+    public static final String string_0171 = "TestString_0171";
+    public static final String string_0172 = "TestString_0172";
+    public static final String string_0173 = "TestString_0173";
+    public static final String string_0174 = "TestString_0174";
+    public static final String string_0175 = "TestString_0175";
+    public static final String string_0176 = "TestString_0176";
+    public static final String string_0177 = "TestString_0177";
+    public static final String string_0178 = "TestString_0178";
+    public static final String string_0179 = "TestString_0179";
+    public static final String string_0180 = "TestString_0180";
+    public static final String string_0181 = "TestString_0181";
+    public static final String string_0182 = "TestString_0182";
+    public static final String string_0183 = "TestString_0183";
+    public static final String string_0184 = "TestString_0184";
+    public static final String string_0185 = "TestString_0185";
+    public static final String string_0186 = "TestString_0186";
+    public static final String string_0187 = "TestString_0187";
+    public static final String string_0188 = "TestString_0188";
+    public static final String string_0189 = "TestString_0189";
+    public static final String string_0190 = "TestString_0190";
+    public static final String string_0191 = "TestString_0191";
+    public static final String string_0192 = "TestString_0192";
+    public static final String string_0193 = "TestString_0193";
+    public static final String string_0194 = "TestString_0194";
+    public static final String string_0195 = "TestString_0195";
+    public static final String string_0196 = "TestString_0196";
+    public static final String string_0197 = "TestString_0197";
+    public static final String string_0198 = "TestString_0198";
+    public static final String string_0199 = "TestString_0199";
+    public static final String string_0200 = "TestString_0200";
+    public static final String string_0201 = "TestString_0201";
+    public static final String string_0202 = "TestString_0202";
+    public static final String string_0203 = "TestString_0203";
+    public static final String string_0204 = "TestString_0204";
+    public static final String string_0205 = "TestString_0205";
+    public static final String string_0206 = "TestString_0206";
+    public static final String string_0207 = "TestString_0207";
+    public static final String string_0208 = "TestString_0208";
+    public static final String string_0209 = "TestString_0209";
+    public static final String string_0210 = "TestString_0210";
+    public static final String string_0211 = "TestString_0211";
+    public static final String string_0212 = "TestString_0212";
+    public static final String string_0213 = "TestString_0213";
+    public static final String string_0214 = "TestString_0214";
+    public static final String string_0215 = "TestString_0215";
+    public static final String string_0216 = "TestString_0216";
+    public static final String string_0217 = "TestString_0217";
+    public static final String string_0218 = "TestString_0218";
+    public static final String string_0219 = "TestString_0219";
+    public static final String string_0220 = "TestString_0220";
+    public static final String string_0221 = "TestString_0221";
+    public static final String string_0222 = "TestString_0222";
+    public static final String string_0223 = "TestString_0223";
+    public static final String string_0224 = "TestString_0224";
+    public static final String string_0225 = "TestString_0225";
+    public static final String string_0226 = "TestString_0226";
+    public static final String string_0227 = "TestString_0227";
+    public static final String string_0228 = "TestString_0228";
+    public static final String string_0229 = "TestString_0229";
+    public static final String string_0230 = "TestString_0230";
+    public static final String string_0231 = "TestString_0231";
+    public static final String string_0232 = "TestString_0232";
+    public static final String string_0233 = "TestString_0233";
+    public static final String string_0234 = "TestString_0234";
+    public static final String string_0235 = "TestString_0235";
+    public static final String string_0236 = "TestString_0236";
+    public static final String string_0237 = "TestString_0237";
+    public static final String string_0238 = "TestString_0238";
+    public static final String string_0239 = "TestString_0239";
+    public static final String string_0240 = "TestString_0240";
+    public static final String string_0241 = "TestString_0241";
+    public static final String string_0242 = "TestString_0242";
+    public static final String string_0243 = "TestString_0243";
+    public static final String string_0244 = "TestString_0244";
+    public static final String string_0245 = "TestString_0245";
+    public static final String string_0246 = "TestString_0246";
+    public static final String string_0247 = "TestString_0247";
+    public static final String string_0248 = "TestString_0248";
+    public static final String string_0249 = "TestString_0249";
+    public static final String string_0250 = "TestString_0250";
+    public static final String string_0251 = "TestString_0251";
+    public static final String string_0252 = "TestString_0252";
+    public static final String string_0253 = "TestString_0253";
+    public static final String string_0254 = "TestString_0254";
+    public static final String string_0255 = "TestString_0255";
+    public static final String string_0256 = "TestString_0256";
+    public static final String string_0257 = "TestString_0257";
+    public static final String string_0258 = "TestString_0258";
+    public static final String string_0259 = "TestString_0259";
+    public static final String string_0260 = "TestString_0260";
+    public static final String string_0261 = "TestString_0261";
+    public static final String string_0262 = "TestString_0262";
+    public static final String string_0263 = "TestString_0263";
+    public static final String string_0264 = "TestString_0264";
+    public static final String string_0265 = "TestString_0265";
+    public static final String string_0266 = "TestString_0266";
+    public static final String string_0267 = "TestString_0267";
+    public static final String string_0268 = "TestString_0268";
+    public static final String string_0269 = "TestString_0269";
+    public static final String string_0270 = "TestString_0270";
+    public static final String string_0271 = "TestString_0271";
+    public static final String string_0272 = "TestString_0272";
+    public static final String string_0273 = "TestString_0273";
+    public static final String string_0274 = "TestString_0274";
+    public static final String string_0275 = "TestString_0275";
+    public static final String string_0276 = "TestString_0276";
+    public static final String string_0277 = "TestString_0277";
+    public static final String string_0278 = "TestString_0278";
+    public static final String string_0279 = "TestString_0279";
+    public static final String string_0280 = "TestString_0280";
+    public static final String string_0281 = "TestString_0281";
+    public static final String string_0282 = "TestString_0282";
+    public static final String string_0283 = "TestString_0283";
+    public static final String string_0284 = "TestString_0284";
+    public static final String string_0285 = "TestString_0285";
+    public static final String string_0286 = "TestString_0286";
+    public static final String string_0287 = "TestString_0287";
+    public static final String string_0288 = "TestString_0288";
+    public static final String string_0289 = "TestString_0289";
+    public static final String string_0290 = "TestString_0290";
+    public static final String string_0291 = "TestString_0291";
+    public static final String string_0292 = "TestString_0292";
+    public static final String string_0293 = "TestString_0293";
+    public static final String string_0294 = "TestString_0294";
+    public static final String string_0295 = "TestString_0295";
+    public static final String string_0296 = "TestString_0296";
+    public static final String string_0297 = "TestString_0297";
+    public static final String string_0298 = "TestString_0298";
+    public static final String string_0299 = "TestString_0299";
+    public static final String string_0300 = "TestString_0300";
+    public static final String string_0301 = "TestString_0301";
+    public static final String string_0302 = "TestString_0302";
+    public static final String string_0303 = "TestString_0303";
+    public static final String string_0304 = "TestString_0304";
+    public static final String string_0305 = "TestString_0305";
+    public static final String string_0306 = "TestString_0306";
+    public static final String string_0307 = "TestString_0307";
+    public static final String string_0308 = "TestString_0308";
+    public static final String string_0309 = "TestString_0309";
+    public static final String string_0310 = "TestString_0310";
+    public static final String string_0311 = "TestString_0311";
+    public static final String string_0312 = "TestString_0312";
+    public static final String string_0313 = "TestString_0313";
+    public static final String string_0314 = "TestString_0314";
+    public static final String string_0315 = "TestString_0315";
+    public static final String string_0316 = "TestString_0316";
+    public static final String string_0317 = "TestString_0317";
+    public static final String string_0318 = "TestString_0318";
+    public static final String string_0319 = "TestString_0319";
+    public static final String string_0320 = "TestString_0320";
+    public static final String string_0321 = "TestString_0321";
+    public static final String string_0322 = "TestString_0322";
+    public static final String string_0323 = "TestString_0323";
+    public static final String string_0324 = "TestString_0324";
+    public static final String string_0325 = "TestString_0325";
+    public static final String string_0326 = "TestString_0326";
+    public static final String string_0327 = "TestString_0327";
+    public static final String string_0328 = "TestString_0328";
+    public static final String string_0329 = "TestString_0329";
+    public static final String string_0330 = "TestString_0330";
+    public static final String string_0331 = "TestString_0331";
+    public static final String string_0332 = "TestString_0332";
+    public static final String string_0333 = "TestString_0333";
+    public static final String string_0334 = "TestString_0334";
+    public static final String string_0335 = "TestString_0335";
+    public static final String string_0336 = "TestString_0336";
+    public static final String string_0337 = "TestString_0337";
+    public static final String string_0338 = "TestString_0338";
+    public static final String string_0339 = "TestString_0339";
+    public static final String string_0340 = "TestString_0340";
+    public static final String string_0341 = "TestString_0341";
+    public static final String string_0342 = "TestString_0342";
+    public static final String string_0343 = "TestString_0343";
+    public static final String string_0344 = "TestString_0344";
+    public static final String string_0345 = "TestString_0345";
+    public static final String string_0346 = "TestString_0346";
+    public static final String string_0347 = "TestString_0347";
+    public static final String string_0348 = "TestString_0348";
+    public static final String string_0349 = "TestString_0349";
+    public static final String string_0350 = "TestString_0350";
+    public static final String string_0351 = "TestString_0351";
+    public static final String string_0352 = "TestString_0352";
+    public static final String string_0353 = "TestString_0353";
+    public static final String string_0354 = "TestString_0354";
+    public static final String string_0355 = "TestString_0355";
+    public static final String string_0356 = "TestString_0356";
+    public static final String string_0357 = "TestString_0357";
+    public static final String string_0358 = "TestString_0358";
+    public static final String string_0359 = "TestString_0359";
+    public static final String string_0360 = "TestString_0360";
+    public static final String string_0361 = "TestString_0361";
+    public static final String string_0362 = "TestString_0362";
+    public static final String string_0363 = "TestString_0363";
+    public static final String string_0364 = "TestString_0364";
+    public static final String string_0365 = "TestString_0365";
+    public static final String string_0366 = "TestString_0366";
+    public static final String string_0367 = "TestString_0367";
+    public static final String string_0368 = "TestString_0368";
+    public static final String string_0369 = "TestString_0369";
+    public static final String string_0370 = "TestString_0370";
+    public static final String string_0371 = "TestString_0371";
+    public static final String string_0372 = "TestString_0372";
+    public static final String string_0373 = "TestString_0373";
+    public static final String string_0374 = "TestString_0374";
+    public static final String string_0375 = "TestString_0375";
+    public static final String string_0376 = "TestString_0376";
+    public static final String string_0377 = "TestString_0377";
+    public static final String string_0378 = "TestString_0378";
+    public static final String string_0379 = "TestString_0379";
+    public static final String string_0380 = "TestString_0380";
+    public static final String string_0381 = "TestString_0381";
+    public static final String string_0382 = "TestString_0382";
+    public static final String string_0383 = "TestString_0383";
+    public static final String string_0384 = "TestString_0384";
+    public static final String string_0385 = "TestString_0385";
+    public static final String string_0386 = "TestString_0386";
+    public static final String string_0387 = "TestString_0387";
+    public static final String string_0388 = "TestString_0388";
+    public static final String string_0389 = "TestString_0389";
+    public static final String string_0390 = "TestString_0390";
+    public static final String string_0391 = "TestString_0391";
+    public static final String string_0392 = "TestString_0392";
+    public static final String string_0393 = "TestString_0393";
+    public static final String string_0394 = "TestString_0394";
+    public static final String string_0395 = "TestString_0395";
+    public static final String string_0396 = "TestString_0396";
+    public static final String string_0397 = "TestString_0397";
+    public static final String string_0398 = "TestString_0398";
+    public static final String string_0399 = "TestString_0399";
+    public static final String string_0400 = "TestString_0400";
+    public static final String string_0401 = "TestString_0401";
+    public static final String string_0402 = "TestString_0402";
+    public static final String string_0403 = "TestString_0403";
+    public static final String string_0404 = "TestString_0404";
+    public static final String string_0405 = "TestString_0405";
+    public static final String string_0406 = "TestString_0406";
+    public static final String string_0407 = "TestString_0407";
+    public static final String string_0408 = "TestString_0408";
+    public static final String string_0409 = "TestString_0409";
+    public static final String string_0410 = "TestString_0410";
+    public static final String string_0411 = "TestString_0411";
+    public static final String string_0412 = "TestString_0412";
+    public static final String string_0413 = "TestString_0413";
+    public static final String string_0414 = "TestString_0414";
+    public static final String string_0415 = "TestString_0415";
+    public static final String string_0416 = "TestString_0416";
+    public static final String string_0417 = "TestString_0417";
+    public static final String string_0418 = "TestString_0418";
+    public static final String string_0419 = "TestString_0419";
+    public static final String string_0420 = "TestString_0420";
+    public static final String string_0421 = "TestString_0421";
+    public static final String string_0422 = "TestString_0422";
+    public static final String string_0423 = "TestString_0423";
+    public static final String string_0424 = "TestString_0424";
+    public static final String string_0425 = "TestString_0425";
+    public static final String string_0426 = "TestString_0426";
+    public static final String string_0427 = "TestString_0427";
+    public static final String string_0428 = "TestString_0428";
+    public static final String string_0429 = "TestString_0429";
+    public static final String string_0430 = "TestString_0430";
+    public static final String string_0431 = "TestString_0431";
+    public static final String string_0432 = "TestString_0432";
+    public static final String string_0433 = "TestString_0433";
+    public static final String string_0434 = "TestString_0434";
+    public static final String string_0435 = "TestString_0435";
+    public static final String string_0436 = "TestString_0436";
+    public static final String string_0437 = "TestString_0437";
+    public static final String string_0438 = "TestString_0438";
+    public static final String string_0439 = "TestString_0439";
+    public static final String string_0440 = "TestString_0440";
+    public static final String string_0441 = "TestString_0441";
+    public static final String string_0442 = "TestString_0442";
+    public static final String string_0443 = "TestString_0443";
+    public static final String string_0444 = "TestString_0444";
+    public static final String string_0445 = "TestString_0445";
+    public static final String string_0446 = "TestString_0446";
+    public static final String string_0447 = "TestString_0447";
+    public static final String string_0448 = "TestString_0448";
+    public static final String string_0449 = "TestString_0449";
+    public static final String string_0450 = "TestString_0450";
+    public static final String string_0451 = "TestString_0451";
+    public static final String string_0452 = "TestString_0452";
+    public static final String string_0453 = "TestString_0453";
+    public static final String string_0454 = "TestString_0454";
+    public static final String string_0455 = "TestString_0455";
+    public static final String string_0456 = "TestString_0456";
+    public static final String string_0457 = "TestString_0457";
+    public static final String string_0458 = "TestString_0458";
+    public static final String string_0459 = "TestString_0459";
+    public static final String string_0460 = "TestString_0460";
+    public static final String string_0461 = "TestString_0461";
+    public static final String string_0462 = "TestString_0462";
+    public static final String string_0463 = "TestString_0463";
+    public static final String string_0464 = "TestString_0464";
+    public static final String string_0465 = "TestString_0465";
+    public static final String string_0466 = "TestString_0466";
+    public static final String string_0467 = "TestString_0467";
+    public static final String string_0468 = "TestString_0468";
+    public static final String string_0469 = "TestString_0469";
+    public static final String string_0470 = "TestString_0470";
+    public static final String string_0471 = "TestString_0471";
+    public static final String string_0472 = "TestString_0472";
+    public static final String string_0473 = "TestString_0473";
+    public static final String string_0474 = "TestString_0474";
+    public static final String string_0475 = "TestString_0475";
+    public static final String string_0476 = "TestString_0476";
+    public static final String string_0477 = "TestString_0477";
+    public static final String string_0478 = "TestString_0478";
+    public static final String string_0479 = "TestString_0479";
+    public static final String string_0480 = "TestString_0480";
+    public static final String string_0481 = "TestString_0481";
+    public static final String string_0482 = "TestString_0482";
+    public static final String string_0483 = "TestString_0483";
+    public static final String string_0484 = "TestString_0484";
+    public static final String string_0485 = "TestString_0485";
+    public static final String string_0486 = "TestString_0486";
+    public static final String string_0487 = "TestString_0487";
+    public static final String string_0488 = "TestString_0488";
+    public static final String string_0489 = "TestString_0489";
+    public static final String string_0490 = "TestString_0490";
+    public static final String string_0491 = "TestString_0491";
+    public static final String string_0492 = "TestString_0492";
+    public static final String string_0493 = "TestString_0493";
+    public static final String string_0494 = "TestString_0494";
+    public static final String string_0495 = "TestString_0495";
+    public static final String string_0496 = "TestString_0496";
+    public static final String string_0497 = "TestString_0497";
+    public static final String string_0498 = "TestString_0498";
+    public static final String string_0499 = "TestString_0499";
+    public static final String string_0500 = "TestString_0500";
+    public static final String string_0501 = "TestString_0501";
+    public static final String string_0502 = "TestString_0502";
+    public static final String string_0503 = "TestString_0503";
+    public static final String string_0504 = "TestString_0504";
+    public static final String string_0505 = "TestString_0505";
+    public static final String string_0506 = "TestString_0506";
+    public static final String string_0507 = "TestString_0507";
+    public static final String string_0508 = "TestString_0508";
+    public static final String string_0509 = "TestString_0509";
+    public static final String string_0510 = "TestString_0510";
+    public static final String string_0511 = "TestString_0511";
+    public static final String string_0512 = "TestString_0512";
+    public static final String string_0513 = "TestString_0513";
+    public static final String string_0514 = "TestString_0514";
+    public static final String string_0515 = "TestString_0515";
+    public static final String string_0516 = "TestString_0516";
+    public static final String string_0517 = "TestString_0517";
+    public static final String string_0518 = "TestString_0518";
+    public static final String string_0519 = "TestString_0519";
+    public static final String string_0520 = "TestString_0520";
+    public static final String string_0521 = "TestString_0521";
+    public static final String string_0522 = "TestString_0522";
+    public static final String string_0523 = "TestString_0523";
+    public static final String string_0524 = "TestString_0524";
+    public static final String string_0525 = "TestString_0525";
+    public static final String string_0526 = "TestString_0526";
+    public static final String string_0527 = "TestString_0527";
+    public static final String string_0528 = "TestString_0528";
+    public static final String string_0529 = "TestString_0529";
+    public static final String string_0530 = "TestString_0530";
+    public static final String string_0531 = "TestString_0531";
+    public static final String string_0532 = "TestString_0532";
+    public static final String string_0533 = "TestString_0533";
+    public static final String string_0534 = "TestString_0534";
+    public static final String string_0535 = "TestString_0535";
+    public static final String string_0536 = "TestString_0536";
+    public static final String string_0537 = "TestString_0537";
+    public static final String string_0538 = "TestString_0538";
+    public static final String string_0539 = "TestString_0539";
+    public static final String string_0540 = "TestString_0540";
+    public static final String string_0541 = "TestString_0541";
+    public static final String string_0542 = "TestString_0542";
+    public static final String string_0543 = "TestString_0543";
+    public static final String string_0544 = "TestString_0544";
+    public static final String string_0545 = "TestString_0545";
+    public static final String string_0546 = "TestString_0546";
+    public static final String string_0547 = "TestString_0547";
+    public static final String string_0548 = "TestString_0548";
+    public static final String string_0549 = "TestString_0549";
+    public static final String string_0550 = "TestString_0550";
+    public static final String string_0551 = "TestString_0551";
+    public static final String string_0552 = "TestString_0552";
+    public static final String string_0553 = "TestString_0553";
+    public static final String string_0554 = "TestString_0554";
+    public static final String string_0555 = "TestString_0555";
+    public static final String string_0556 = "TestString_0556";
+    public static final String string_0557 = "TestString_0557";
+    public static final String string_0558 = "TestString_0558";
+    public static final String string_0559 = "TestString_0559";
+    public static final String string_0560 = "TestString_0560";
+    public static final String string_0561 = "TestString_0561";
+    public static final String string_0562 = "TestString_0562";
+    public static final String string_0563 = "TestString_0563";
+    public static final String string_0564 = "TestString_0564";
+    public static final String string_0565 = "TestString_0565";
+    public static final String string_0566 = "TestString_0566";
+    public static final String string_0567 = "TestString_0567";
+    public static final String string_0568 = "TestString_0568";
+    public static final String string_0569 = "TestString_0569";
+    public static final String string_0570 = "TestString_0570";
+    public static final String string_0571 = "TestString_0571";
+    public static final String string_0572 = "TestString_0572";
+    public static final String string_0573 = "TestString_0573";
+    public static final String string_0574 = "TestString_0574";
+    public static final String string_0575 = "TestString_0575";
+    public static final String string_0576 = "TestString_0576";
+    public static final String string_0577 = "TestString_0577";
+    public static final String string_0578 = "TestString_0578";
+    public static final String string_0579 = "TestString_0579";
+    public static final String string_0580 = "TestString_0580";
+    public static final String string_0581 = "TestString_0581";
+    public static final String string_0582 = "TestString_0582";
+    public static final String string_0583 = "TestString_0583";
+    public static final String string_0584 = "TestString_0584";
+    public static final String string_0585 = "TestString_0585";
+    public static final String string_0586 = "TestString_0586";
+    public static final String string_0587 = "TestString_0587";
+    public static final String string_0588 = "TestString_0588";
+    public static final String string_0589 = "TestString_0589";
+    public static final String string_0590 = "TestString_0590";
+    public static final String string_0591 = "TestString_0591";
+    public static final String string_0592 = "TestString_0592";
+    public static final String string_0593 = "TestString_0593";
+    public static final String string_0594 = "TestString_0594";
+    public static final String string_0595 = "TestString_0595";
+    public static final String string_0596 = "TestString_0596";
+    public static final String string_0597 = "TestString_0597";
+    public static final String string_0598 = "TestString_0598";
+    public static final String string_0599 = "TestString_0599";
+    public static final String string_0600 = "TestString_0600";
+    public static final String string_0601 = "TestString_0601";
+    public static final String string_0602 = "TestString_0602";
+    public static final String string_0603 = "TestString_0603";
+    public static final String string_0604 = "TestString_0604";
+    public static final String string_0605 = "TestString_0605";
+    public static final String string_0606 = "TestString_0606";
+    public static final String string_0607 = "TestString_0607";
+    public static final String string_0608 = "TestString_0608";
+    public static final String string_0609 = "TestString_0609";
+    public static final String string_0610 = "TestString_0610";
+    public static final String string_0611 = "TestString_0611";
+    public static final String string_0612 = "TestString_0612";
+    public static final String string_0613 = "TestString_0613";
+    public static final String string_0614 = "TestString_0614";
+    public static final String string_0615 = "TestString_0615";
+    public static final String string_0616 = "TestString_0616";
+    public static final String string_0617 = "TestString_0617";
+    public static final String string_0618 = "TestString_0618";
+    public static final String string_0619 = "TestString_0619";
+    public static final String string_0620 = "TestString_0620";
+    public static final String string_0621 = "TestString_0621";
+    public static final String string_0622 = "TestString_0622";
+    public static final String string_0623 = "TestString_0623";
+    public static final String string_0624 = "TestString_0624";
+    public static final String string_0625 = "TestString_0625";
+    public static final String string_0626 = "TestString_0626";
+    public static final String string_0627 = "TestString_0627";
+    public static final String string_0628 = "TestString_0628";
+    public static final String string_0629 = "TestString_0629";
+    public static final String string_0630 = "TestString_0630";
+    public static final String string_0631 = "TestString_0631";
+    public static final String string_0632 = "TestString_0632";
+    public static final String string_0633 = "TestString_0633";
+    public static final String string_0634 = "TestString_0634";
+    public static final String string_0635 = "TestString_0635";
+    public static final String string_0636 = "TestString_0636";
+    public static final String string_0637 = "TestString_0637";
+    public static final String string_0638 = "TestString_0638";
+    public static final String string_0639 = "TestString_0639";
+    public static final String string_0640 = "TestString_0640";
+    public static final String string_0641 = "TestString_0641";
+    public static final String string_0642 = "TestString_0642";
+    public static final String string_0643 = "TestString_0643";
+    public static final String string_0644 = "TestString_0644";
+    public static final String string_0645 = "TestString_0645";
+    public static final String string_0646 = "TestString_0646";
+    public static final String string_0647 = "TestString_0647";
+    public static final String string_0648 = "TestString_0648";
+    public static final String string_0649 = "TestString_0649";
+    public static final String string_0650 = "TestString_0650";
+    public static final String string_0651 = "TestString_0651";
+    public static final String string_0652 = "TestString_0652";
+    public static final String string_0653 = "TestString_0653";
+    public static final String string_0654 = "TestString_0654";
+    public static final String string_0655 = "TestString_0655";
+    public static final String string_0656 = "TestString_0656";
+    public static final String string_0657 = "TestString_0657";
+    public static final String string_0658 = "TestString_0658";
+    public static final String string_0659 = "TestString_0659";
+    public static final String string_0660 = "TestString_0660";
+    public static final String string_0661 = "TestString_0661";
+    public static final String string_0662 = "TestString_0662";
+    public static final String string_0663 = "TestString_0663";
+    public static final String string_0664 = "TestString_0664";
+    public static final String string_0665 = "TestString_0665";
+    public static final String string_0666 = "TestString_0666";
+    public static final String string_0667 = "TestString_0667";
+    public static final String string_0668 = "TestString_0668";
+    public static final String string_0669 = "TestString_0669";
+    public static final String string_0670 = "TestString_0670";
+    public static final String string_0671 = "TestString_0671";
+    public static final String string_0672 = "TestString_0672";
+    public static final String string_0673 = "TestString_0673";
+    public static final String string_0674 = "TestString_0674";
+    public static final String string_0675 = "TestString_0675";
+    public static final String string_0676 = "TestString_0676";
+    public static final String string_0677 = "TestString_0677";
+    public static final String string_0678 = "TestString_0678";
+    public static final String string_0679 = "TestString_0679";
+    public static final String string_0680 = "TestString_0680";
+    public static final String string_0681 = "TestString_0681";
+    public static final String string_0682 = "TestString_0682";
+    public static final String string_0683 = "TestString_0683";
+    public static final String string_0684 = "TestString_0684";
+    public static final String string_0685 = "TestString_0685";
+    public static final String string_0686 = "TestString_0686";
+    public static final String string_0687 = "TestString_0687";
+    public static final String string_0688 = "TestString_0688";
+    public static final String string_0689 = "TestString_0689";
+    public static final String string_0690 = "TestString_0690";
+    public static final String string_0691 = "TestString_0691";
+    public static final String string_0692 = "TestString_0692";
+    public static final String string_0693 = "TestString_0693";
+    public static final String string_0694 = "TestString_0694";
+    public static final String string_0695 = "TestString_0695";
+    public static final String string_0696 = "TestString_0696";
+    public static final String string_0697 = "TestString_0697";
+    public static final String string_0698 = "TestString_0698";
+    public static final String string_0699 = "TestString_0699";
+    public static final String string_0700 = "TestString_0700";
+    public static final String string_0701 = "TestString_0701";
+    public static final String string_0702 = "TestString_0702";
+    public static final String string_0703 = "TestString_0703";
+    public static final String string_0704 = "TestString_0704";
+    public static final String string_0705 = "TestString_0705";
+    public static final String string_0706 = "TestString_0706";
+    public static final String string_0707 = "TestString_0707";
+    public static final String string_0708 = "TestString_0708";
+    public static final String string_0709 = "TestString_0709";
+    public static final String string_0710 = "TestString_0710";
+    public static final String string_0711 = "TestString_0711";
+    public static final String string_0712 = "TestString_0712";
+    public static final String string_0713 = "TestString_0713";
+    public static final String string_0714 = "TestString_0714";
+    public static final String string_0715 = "TestString_0715";
+    public static final String string_0716 = "TestString_0716";
+    public static final String string_0717 = "TestString_0717";
+    public static final String string_0718 = "TestString_0718";
+    public static final String string_0719 = "TestString_0719";
+    public static final String string_0720 = "TestString_0720";
+    public static final String string_0721 = "TestString_0721";
+    public static final String string_0722 = "TestString_0722";
+    public static final String string_0723 = "TestString_0723";
+    public static final String string_0724 = "TestString_0724";
+    public static final String string_0725 = "TestString_0725";
+    public static final String string_0726 = "TestString_0726";
+    public static final String string_0727 = "TestString_0727";
+    public static final String string_0728 = "TestString_0728";
+    public static final String string_0729 = "TestString_0729";
+    public static final String string_0730 = "TestString_0730";
+    public static final String string_0731 = "TestString_0731";
+    public static final String string_0732 = "TestString_0732";
+    public static final String string_0733 = "TestString_0733";
+    public static final String string_0734 = "TestString_0734";
+    public static final String string_0735 = "TestString_0735";
+    public static final String string_0736 = "TestString_0736";
+    public static final String string_0737 = "TestString_0737";
+    public static final String string_0738 = "TestString_0738";
+    public static final String string_0739 = "TestString_0739";
+    public static final String string_0740 = "TestString_0740";
+    public static final String string_0741 = "TestString_0741";
+    public static final String string_0742 = "TestString_0742";
+    public static final String string_0743 = "TestString_0743";
+    public static final String string_0744 = "TestString_0744";
+    public static final String string_0745 = "TestString_0745";
+    public static final String string_0746 = "TestString_0746";
+    public static final String string_0747 = "TestString_0747";
+    public static final String string_0748 = "TestString_0748";
+    public static final String string_0749 = "TestString_0749";
+    public static final String string_0750 = "TestString_0750";
+    public static final String string_0751 = "TestString_0751";
+    public static final String string_0752 = "TestString_0752";
+    public static final String string_0753 = "TestString_0753";
+    public static final String string_0754 = "TestString_0754";
+    public static final String string_0755 = "TestString_0755";
+    public static final String string_0756 = "TestString_0756";
+    public static final String string_0757 = "TestString_0757";
+    public static final String string_0758 = "TestString_0758";
+    public static final String string_0759 = "TestString_0759";
+    public static final String string_0760 = "TestString_0760";
+    public static final String string_0761 = "TestString_0761";
+    public static final String string_0762 = "TestString_0762";
+    public static final String string_0763 = "TestString_0763";
+    public static final String string_0764 = "TestString_0764";
+    public static final String string_0765 = "TestString_0765";
+    public static final String string_0766 = "TestString_0766";
+    public static final String string_0767 = "TestString_0767";
+    public static final String string_0768 = "TestString_0768";
+    public static final String string_0769 = "TestString_0769";
+    public static final String string_0770 = "TestString_0770";
+    public static final String string_0771 = "TestString_0771";
+    public static final String string_0772 = "TestString_0772";
+    public static final String string_0773 = "TestString_0773";
+    public static final String string_0774 = "TestString_0774";
+    public static final String string_0775 = "TestString_0775";
+    public static final String string_0776 = "TestString_0776";
+    public static final String string_0777 = "TestString_0777";
+    public static final String string_0778 = "TestString_0778";
+    public static final String string_0779 = "TestString_0779";
+    public static final String string_0780 = "TestString_0780";
+    public static final String string_0781 = "TestString_0781";
+    public static final String string_0782 = "TestString_0782";
+    public static final String string_0783 = "TestString_0783";
+    public static final String string_0784 = "TestString_0784";
+    public static final String string_0785 = "TestString_0785";
+    public static final String string_0786 = "TestString_0786";
+    public static final String string_0787 = "TestString_0787";
+    public static final String string_0788 = "TestString_0788";
+    public static final String string_0789 = "TestString_0789";
+    public static final String string_0790 = "TestString_0790";
+    public static final String string_0791 = "TestString_0791";
+    public static final String string_0792 = "TestString_0792";
+    public static final String string_0793 = "TestString_0793";
+    public static final String string_0794 = "TestString_0794";
+    public static final String string_0795 = "TestString_0795";
+    public static final String string_0796 = "TestString_0796";
+    public static final String string_0797 = "TestString_0797";
+    public static final String string_0798 = "TestString_0798";
+    public static final String string_0799 = "TestString_0799";
+    public static final String string_0800 = "TestString_0800";
+    public static final String string_0801 = "TestString_0801";
+    public static final String string_0802 = "TestString_0802";
+    public static final String string_0803 = "TestString_0803";
+    public static final String string_0804 = "TestString_0804";
+    public static final String string_0805 = "TestString_0805";
+    public static final String string_0806 = "TestString_0806";
+    public static final String string_0807 = "TestString_0807";
+    public static final String string_0808 = "TestString_0808";
+    public static final String string_0809 = "TestString_0809";
+    public static final String string_0810 = "TestString_0810";
+    public static final String string_0811 = "TestString_0811";
+    public static final String string_0812 = "TestString_0812";
+    public static final String string_0813 = "TestString_0813";
+    public static final String string_0814 = "TestString_0814";
+    public static final String string_0815 = "TestString_0815";
+    public static final String string_0816 = "TestString_0816";
+    public static final String string_0817 = "TestString_0817";
+    public static final String string_0818 = "TestString_0818";
+    public static final String string_0819 = "TestString_0819";
+    public static final String string_0820 = "TestString_0820";
+    public static final String string_0821 = "TestString_0821";
+    public static final String string_0822 = "TestString_0822";
+    public static final String string_0823 = "TestString_0823";
+    public static final String string_0824 = "TestString_0824";
+    public static final String string_0825 = "TestString_0825";
+    public static final String string_0826 = "TestString_0826";
+    public static final String string_0827 = "TestString_0827";
+    public static final String string_0828 = "TestString_0828";
+    public static final String string_0829 = "TestString_0829";
+    public static final String string_0830 = "TestString_0830";
+    public static final String string_0831 = "TestString_0831";
+    public static final String string_0832 = "TestString_0832";
+    public static final String string_0833 = "TestString_0833";
+    public static final String string_0834 = "TestString_0834";
+    public static final String string_0835 = "TestString_0835";
+    public static final String string_0836 = "TestString_0836";
+    public static final String string_0837 = "TestString_0837";
+    public static final String string_0838 = "TestString_0838";
+    public static final String string_0839 = "TestString_0839";
+    public static final String string_0840 = "TestString_0840";
+    public static final String string_0841 = "TestString_0841";
+    public static final String string_0842 = "TestString_0842";
+    public static final String string_0843 = "TestString_0843";
+    public static final String string_0844 = "TestString_0844";
+    public static final String string_0845 = "TestString_0845";
+    public static final String string_0846 = "TestString_0846";
+    public static final String string_0847 = "TestString_0847";
+    public static final String string_0848 = "TestString_0848";
+    public static final String string_0849 = "TestString_0849";
+    public static final String string_0850 = "TestString_0850";
+    public static final String string_0851 = "TestString_0851";
+    public static final String string_0852 = "TestString_0852";
+    public static final String string_0853 = "TestString_0853";
+    public static final String string_0854 = "TestString_0854";
+    public static final String string_0855 = "TestString_0855";
+    public static final String string_0856 = "TestString_0856";
+    public static final String string_0857 = "TestString_0857";
+    public static final String string_0858 = "TestString_0858";
+    public static final String string_0859 = "TestString_0859";
+    public static final String string_0860 = "TestString_0860";
+    public static final String string_0861 = "TestString_0861";
+    public static final String string_0862 = "TestString_0862";
+    public static final String string_0863 = "TestString_0863";
+    public static final String string_0864 = "TestString_0864";
+    public static final String string_0865 = "TestString_0865";
+    public static final String string_0866 = "TestString_0866";
+    public static final String string_0867 = "TestString_0867";
+    public static final String string_0868 = "TestString_0868";
+    public static final String string_0869 = "TestString_0869";
+    public static final String string_0870 = "TestString_0870";
+    public static final String string_0871 = "TestString_0871";
+    public static final String string_0872 = "TestString_0872";
+    public static final String string_0873 = "TestString_0873";
+    public static final String string_0874 = "TestString_0874";
+    public static final String string_0875 = "TestString_0875";
+    public static final String string_0876 = "TestString_0876";
+    public static final String string_0877 = "TestString_0877";
+    public static final String string_0878 = "TestString_0878";
+    public static final String string_0879 = "TestString_0879";
+    public static final String string_0880 = "TestString_0880";
+    public static final String string_0881 = "TestString_0881";
+    public static final String string_0882 = "TestString_0882";
+    public static final String string_0883 = "TestString_0883";
+    public static final String string_0884 = "TestString_0884";
+    public static final String string_0885 = "TestString_0885";
+    public static final String string_0886 = "TestString_0886";
+    public static final String string_0887 = "TestString_0887";
+    public static final String string_0888 = "TestString_0888";
+    public static final String string_0889 = "TestString_0889";
+    public static final String string_0890 = "TestString_0890";
+    public static final String string_0891 = "TestString_0891";
+    public static final String string_0892 = "TestString_0892";
+    public static final String string_0893 = "TestString_0893";
+    public static final String string_0894 = "TestString_0894";
+    public static final String string_0895 = "TestString_0895";
+    public static final String string_0896 = "TestString_0896";
+    public static final String string_0897 = "TestString_0897";
+    public static final String string_0898 = "TestString_0898";
+    public static final String string_0899 = "TestString_0899";
+    public static final String string_0900 = "TestString_0900";
+    public static final String string_0901 = "TestString_0901";
+    public static final String string_0902 = "TestString_0902";
+    public static final String string_0903 = "TestString_0903";
+    public static final String string_0904 = "TestString_0904";
+    public static final String string_0905 = "TestString_0905";
+    public static final String string_0906 = "TestString_0906";
+    public static final String string_0907 = "TestString_0907";
+    public static final String string_0908 = "TestString_0908";
+    public static final String string_0909 = "TestString_0909";
+    public static final String string_0910 = "TestString_0910";
+    public static final String string_0911 = "TestString_0911";
+    public static final String string_0912 = "TestString_0912";
+    public static final String string_0913 = "TestString_0913";
+    public static final String string_0914 = "TestString_0914";
+    public static final String string_0915 = "TestString_0915";
+    public static final String string_0916 = "TestString_0916";
+    public static final String string_0917 = "TestString_0917";
+    public static final String string_0918 = "TestString_0918";
+    public static final String string_0919 = "TestString_0919";
+    public static final String string_0920 = "TestString_0920";
+    public static final String string_0921 = "TestString_0921";
+    public static final String string_0922 = "TestString_0922";
+    public static final String string_0923 = "TestString_0923";
+    public static final String string_0924 = "TestString_0924";
+    public static final String string_0925 = "TestString_0925";
+    public static final String string_0926 = "TestString_0926";
+    public static final String string_0927 = "TestString_0927";
+    public static final String string_0928 = "TestString_0928";
+    public static final String string_0929 = "TestString_0929";
+    public static final String string_0930 = "TestString_0930";
+    public static final String string_0931 = "TestString_0931";
+    public static final String string_0932 = "TestString_0932";
+    public static final String string_0933 = "TestString_0933";
+    public static final String string_0934 = "TestString_0934";
+    public static final String string_0935 = "TestString_0935";
+    public static final String string_0936 = "TestString_0936";
+    public static final String string_0937 = "TestString_0937";
+    public static final String string_0938 = "TestString_0938";
+    public static final String string_0939 = "TestString_0939";
+    public static final String string_0940 = "TestString_0940";
+    public static final String string_0941 = "TestString_0941";
+    public static final String string_0942 = "TestString_0942";
+    public static final String string_0943 = "TestString_0943";
+    public static final String string_0944 = "TestString_0944";
+    public static final String string_0945 = "TestString_0945";
+    public static final String string_0946 = "TestString_0946";
+    public static final String string_0947 = "TestString_0947";
+    public static final String string_0948 = "TestString_0948";
+    public static final String string_0949 = "TestString_0949";
+    public static final String string_0950 = "TestString_0950";
+    public static final String string_0951 = "TestString_0951";
+    public static final String string_0952 = "TestString_0952";
+    public static final String string_0953 = "TestString_0953";
+    public static final String string_0954 = "TestString_0954";
+    public static final String string_0955 = "TestString_0955";
+    public static final String string_0956 = "TestString_0956";
+    public static final String string_0957 = "TestString_0957";
+    public static final String string_0958 = "TestString_0958";
+    public static final String string_0959 = "TestString_0959";
+    public static final String string_0960 = "TestString_0960";
+    public static final String string_0961 = "TestString_0961";
+    public static final String string_0962 = "TestString_0962";
+    public static final String string_0963 = "TestString_0963";
+    public static final String string_0964 = "TestString_0964";
+    public static final String string_0965 = "TestString_0965";
+    public static final String string_0966 = "TestString_0966";
+    public static final String string_0967 = "TestString_0967";
+    public static final String string_0968 = "TestString_0968";
+    public static final String string_0969 = "TestString_0969";
+    public static final String string_0970 = "TestString_0970";
+    public static final String string_0971 = "TestString_0971";
+    public static final String string_0972 = "TestString_0972";
+    public static final String string_0973 = "TestString_0973";
+    public static final String string_0974 = "TestString_0974";
+    public static final String string_0975 = "TestString_0975";
+    public static final String string_0976 = "TestString_0976";
+    public static final String string_0977 = "TestString_0977";
+    public static final String string_0978 = "TestString_0978";
+    public static final String string_0979 = "TestString_0979";
+    public static final String string_0980 = "TestString_0980";
+    public static final String string_0981 = "TestString_0981";
+    public static final String string_0982 = "TestString_0982";
+    public static final String string_0983 = "TestString_0983";
+    public static final String string_0984 = "TestString_0984";
+    public static final String string_0985 = "TestString_0985";
+    public static final String string_0986 = "TestString_0986";
+    public static final String string_0987 = "TestString_0987";
+    public static final String string_0988 = "TestString_0988";
+    public static final String string_0989 = "TestString_0989";
+    public static final String string_0990 = "TestString_0990";
+    public static final String string_0991 = "TestString_0991";
+    public static final String string_0992 = "TestString_0992";
+    public static final String string_0993 = "TestString_0993";
+    public static final String string_0994 = "TestString_0994";
+    public static final String string_0995 = "TestString_0995";
+    public static final String string_0996 = "TestString_0996";
+    public static final String string_0997 = "TestString_0997";
+    public static final String string_0998 = "TestString_0998";
+    public static final String string_0999 = "TestString_0999";
+    public static final String string_1000 = "TestString_1000";
+    public static final String string_1001 = "TestString_1001";
+    public static final String string_1002 = "TestString_1002";
+    public static final String string_1003 = "TestString_1003";
+    public static final String string_1004 = "TestString_1004";
+    public static final String string_1005 = "TestString_1005";
+    public static final String string_1006 = "TestString_1006";
+    public static final String string_1007 = "TestString_1007";
+    public static final String string_1008 = "TestString_1008";
+    public static final String string_1009 = "TestString_1009";
+    public static final String string_1010 = "TestString_1010";
+    public static final String string_1011 = "TestString_1011";
+    public static final String string_1012 = "TestString_1012";
+    public static final String string_1013 = "TestString_1013";
+    public static final String string_1014 = "TestString_1014";
+    public static final String string_1015 = "TestString_1015";
+    public static final String string_1016 = "TestString_1016";
+    public static final String string_1017 = "TestString_1017";
+    public static final String string_1018 = "TestString_1018";
+    public static final String string_1019 = "TestString_1019";
+    public static final String string_1020 = "TestString_1020";
+    public static final String string_1021 = "TestString_1021";
+    public static final String string_1022 = "TestString_1022";
+    public static final String string_1023 = "TestString_1023";
+    public static final String string_1024 = "TestString_1024";
+
+    public void timeConstStringsWithConflict(int count) {
+      for (int i = 0; i < count; ++i) {
+        $noinline$foo("TestString_0000");
+        $noinline$foo("TestString_1024");
+      }
+    }
+
+    public void timeConstStringsWithoutConflict(int count) {
+      for (int i = 0; i < count; ++i) {
+        $noinline$foo("TestString_0001");
+        $noinline$foo("TestString_1023");
+      }
+    }
+
+    static void $noinline$foo(String s) {
+      if (doThrow) { throw new Error(); }
+    }
+
+    public static boolean doThrow = false;
+}
diff --git a/benchmark/jni-perf/perf_jni.cc b/benchmark/jni-perf/perf_jni.cc
index cd8d520..06dded8 100644
--- a/benchmark/jni-perf/perf_jni.cc
+++ b/benchmark/jni-perf/perf_jni.cc
@@ -17,7 +17,7 @@
 #include <assert.h>
 
 #include "jni.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc
index e7ca9eb..4b2c024 100644
--- a/benchmark/jobject-benchmark/jobject_benchmark.cc
+++ b/benchmark/jobject-benchmark/jobject_benchmark.cc
@@ -16,8 +16,9 @@
 
 #include "jni.h"
 
+#include "java_vm_ext.h"
 #include "mirror/class-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace {
@@ -25,10 +26,10 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveLocal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   for (jint i = 0; i < reps; ++i) {
-    jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
+    jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Decode());
     soa.Env()->DeleteLocalRef(ref);
   }
 }
@@ -36,11 +37,11 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeLocal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
-  jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
+  jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Decode());
   for (jint i = 0; i < reps; ++i) {
-    CHECK_EQ(soa.Decode<mirror::Object*>(ref), obj);
+    CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
   }
   soa.Env()->DeleteLocalRef(ref);
 }
@@ -48,7 +49,7 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveGlobal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   for (jint i = 0; i < reps; ++i) {
     jobject ref = soa.Vm()->AddGlobalRef(soa.Self(), obj);
@@ -59,11 +60,11 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeGlobal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   jobject ref = soa.Vm()->AddGlobalRef(soa.Self(), obj);
   for (jint i = 0; i < reps; ++i) {
-    CHECK_EQ(soa.Decode<mirror::Object*>(ref), obj);
+    CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
   }
   soa.Vm()->DeleteGlobalRef(soa.Self(), ref);
 }
@@ -71,7 +72,7 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeAddRemoveWeakGlobal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   for (jint i = 0; i < reps; ++i) {
     jobject ref = soa.Vm()->AddWeakGlobalRef(soa.Self(), obj);
@@ -82,11 +83,11 @@
 extern "C" JNIEXPORT void JNICALL Java_JObjectBenchmark_timeDecodeWeakGlobal(
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   jobject ref = soa.Vm()->AddWeakGlobalRef(soa.Self(), obj);
   for (jint i = 0; i < reps; ++i) {
-    CHECK_EQ(soa.Decode<mirror::Object*>(ref), obj);
+    CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
   }
   soa.Vm()->DeleteWeakGlobalRef(soa.Self(), ref);
 }
@@ -95,7 +96,7 @@
     JNIEnv* env, jobject jobj, jint reps) {
   ScopedObjectAccess soa(env);
   for (jint i = 0; i < reps; ++i) {
-    soa.Decode<mirror::Object*>(jobj);
+    soa.Decode<mirror::Object>(jobj);
   }
 }
 
diff --git a/build/Android.bp b/build/Android.bp
index 4be43ec..9156027 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -22,8 +22,6 @@
     name: "art_defaults",
     clang: true,
     cflags: [
-        "-O3",
-
         // Base set of cflags used by all things ART.
         "-fno-rtti",
         "-ggdb3",
@@ -149,10 +147,9 @@
     ],
 }
 
-cc_defaults {
+art_debug_defaults {
     name: "art_debug_defaults",
     cflags: [
-        "-O2",
         "-DDYNAMIC_ANNOTATIONS_ENABLED=1",
         "-DVIXL_DEBUG",
         "-UNDEBUG",
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 6befec5..b0fa124 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -26,7 +26,6 @@
   # Mac OS doesn't support low-4GB allocation in a 64-bit process. So we won't be able to create
   # our heaps.
   ART_HOST_SUPPORTED_ARCH := x86
-  ART_MULTILIB_OVERRIDE_host := 32
 endif
 
 ART_COVERAGE := false
@@ -59,27 +58,19 @@
   ifneq ($(filter %64,$(TARGET_ARCH)),)
     ART_PHONY_TEST_TARGET_SUFFIX := 64
     2ND_ART_PHONY_TEST_TARGET_SUFFIX := 32
-    ART_TARGET_ARCH_32 := $(TARGET_2ND_ARCH)
-    ART_TARGET_ARCH_64 := $(TARGET_ARCH)
   else
     # TODO: ???
     $(warning Do not know what to do with this multi-target configuration!)
     ART_PHONY_TEST_TARGET_SUFFIX := 32
     2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
-    ART_TARGET_ARCH_32 := $(TARGET_ARCH)
-    ART_TARGET_ARCH_64 :=
   endif
 else
   ifneq ($(filter %64,$(TARGET_ARCH)),)
     ART_PHONY_TEST_TARGET_SUFFIX := 64
     2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
-    ART_TARGET_ARCH_32 :=
-    ART_TARGET_ARCH_64 := $(TARGET_ARCH)
   else
     ART_PHONY_TEST_TARGET_SUFFIX := 32
     2ND_ART_PHONY_TEST_TARGET_SUFFIX :=
-    ART_TARGET_ARCH_32 := $(TARGET_ARCH)
-    ART_TARGET_ARCH_64 :=
   endif
 endif
 
@@ -88,23 +79,17 @@
 ifeq ($(HOST_PREFER_32_BIT),true)
   ART_PHONY_TEST_HOST_SUFFIX := 32
   2ND_ART_PHONY_TEST_HOST_SUFFIX :=
-  ART_HOST_ARCH_32 := x86
-  ART_HOST_ARCH_64 :=
   ART_HOST_ARCH := x86
   2ND_ART_HOST_ARCH :=
   2ND_HOST_ARCH :=
-  ART_HOST_LIBRARY_PATH := $(HOST_LIBRARY_PATH)
   ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
   2ND_ART_HOST_OUT_SHARED_LIBRARIES :=
 else
   ART_PHONY_TEST_HOST_SUFFIX := 64
   2ND_ART_PHONY_TEST_HOST_SUFFIX := 32
-  ART_HOST_ARCH_32 := x86
-  ART_HOST_ARCH_64 := x86_64
   ART_HOST_ARCH := x86_64
   2ND_ART_HOST_ARCH := x86
   2ND_HOST_ARCH := x86
-  ART_HOST_LIBRARY_PATH := $(HOST_LIBRARY_PATH)
   ART_HOST_OUT_SHARED_LIBRARIES := $(HOST_OUT_SHARED_LIBRARIES)
   2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
 endif
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 7edc1cc..4c82506 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -18,7 +18,6 @@
 ART_ANDROID_COMMON_BUILD_MK = true
 
 include art/build/Android.common.mk
-include art/build/Android.common_utils.mk
 
 # These can be overridden via the environment or by editing to
 # enable/disable certain build configuration.
@@ -34,26 +33,6 @@
 ART_BUILD_HOST_NDEBUG ?= true
 ART_BUILD_HOST_DEBUG ?= true
 
-# Set this to change what opt level ART is built at.
-ART_DEBUG_OPT_FLAG ?= -O2
-ART_NDEBUG_OPT_FLAG ?= -O3
-
-# Enable the static builds only for checkbuilds.
-ifneq (,$(filter checkbuild,$(MAKECMDGOALS)))
-  ART_BUILD_HOST_STATIC ?= true
-else
-  ART_BUILD_HOST_STATIC ?= false
-endif
-
-# Asan does not support static linkage
-ifdef SANITIZE_HOST
-  ART_BUILD_HOST_STATIC := false
-endif
-
-ifneq ($(HOST_OS),linux)
-  ART_BUILD_HOST_STATIC := false
-endif
-
 ifeq ($(ART_BUILD_TARGET_NDEBUG),false)
 $(info Disabling ART_BUILD_TARGET_NDEBUG)
 endif
@@ -66,375 +45,31 @@
 ifeq ($(ART_BUILD_HOST_DEBUG),false)
 $(info Disabling ART_BUILD_HOST_DEBUG)
 endif
-ifeq ($(ART_BUILD_HOST_STATIC),true)
-$(info Enabling ART_BUILD_HOST_STATIC)
-endif
-
-ifeq ($(ART_TEST_DEBUG_GC),true)
-  ART_DEFAULT_GC_TYPE := SS
-  ART_USE_TLAB := true
-endif
-
-#
-# Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
-#
-ART_DEFAULT_GC_TYPE ?= CMS
-art_default_gc_type_cflags := -DART_DEFAULT_GC_TYPE_IS_$(ART_DEFAULT_GC_TYPE)
-
-ART_HOST_CLANG := true
-ART_TARGET_CLANG := true
 
 ART_CPP_EXTENSION := .cc
 
-ART_C_INCLUDES := \
-  external/icu/icu4c/source/common \
-  external/lz4/lib \
-  external/valgrind/include \
-  external/valgrind \
-  external/vixl/src \
-  external/zlib \
-
-# We optimize Thread::Current() with a direct TLS access. This requires access to a private
-# Bionic header.
-# Note: technically we only need this on device, but this avoids the duplication of the includes.
-ART_C_INCLUDES += bionic/libc/private
-
-art_cflags :=
-
-# Warn about thread safety violations with clang.
-art_cflags += -Wthread-safety -Wthread-safety-negative
-
-# Warn if switch fallthroughs aren't annotated.
-art_cflags += -Wimplicit-fallthrough
-
-# Enable float equality warnings.
-art_cflags += -Wfloat-equal
-
-# Enable warning of converting ints to void*.
-art_cflags += -Wint-to-void-pointer-cast
-
-# Enable warning of wrong unused annotations.
-art_cflags += -Wused-but-marked-unused
-
-# Enable warning for deprecated language features.
-art_cflags += -Wdeprecated
-
-# Enable warning for unreachable break & return.
-art_cflags += -Wunreachable-code-break -Wunreachable-code-return
-
-# Bug: http://b/29823425  Disable -Wconstant-conversion and
-# -Wundefined-var-template for Clang update to r271374
-art_cflags += -Wno-constant-conversion -Wno-undefined-var-template
-
-# Enable missing-noreturn only on non-Mac. As lots of things are not implemented for Apple, it's
-# a pain.
-ifneq ($(HOST_OS),darwin)
-  art_cflags += -Wmissing-noreturn
-endif
-
-# Base set of cflags used by all things ART.
-art_cflags += \
-  -fno-rtti \
-  -ggdb3 \
-  -Wall \
-  -Werror \
-  -Wextra \
-  -Wstrict-aliasing \
-  -fstrict-aliasing \
-  -Wunreachable-code \
-  -Wredundant-decls \
-  -Wshadow \
-  -Wunused \
-  -fvisibility=protected \
-  $(art_default_gc_type_cflags)
-
-# The architectures the compiled tools are able to run on. Setting this to 'all' will cause all
-# architectures to be included.
-ART_TARGET_CODEGEN_ARCHS ?= svelte
-ART_HOST_CODEGEN_ARCHS ?= all
-
-ifeq ($(ART_TARGET_CODEGEN_ARCHS),all)
-  ART_TARGET_CODEGEN_ARCHS := $(sort $(ART_TARGET_SUPPORTED_ARCH) $(ART_HOST_SUPPORTED_ARCH))
-else
-  ifeq ($(ART_TARGET_CODEGEN_ARCHS),svelte)
-    ART_TARGET_CODEGEN_ARCHS := $(sort $(ART_TARGET_ARCH_64) $(ART_TARGET_ARCH_32))
-  endif
-endif
-ifeq ($(ART_HOST_CODEGEN_ARCHS),all)
-  ART_HOST_CODEGEN_ARCHS := $(sort $(ART_TARGET_SUPPORTED_ARCH) $(ART_HOST_SUPPORTED_ARCH))
-else
-  ifeq ($(ART_HOST_CODEGEN_ARCHS),svelte)
-    ART_HOST_CODEGEN_ARCHS := $(sort $(ART_TARGET_CODEGEN_ARCHS) $(ART_HOST_ARCH_64) $(ART_HOST_ARCH_32))
-  endif
-endif
-
-ifneq (,$(filter arm64,$(ART_TARGET_CODEGEN_ARCHS)))
-  ART_TARGET_CODEGEN_ARCHS += arm
-endif
-ifneq (,$(filter mips64,$(ART_TARGET_CODEGEN_ARCHS)))
-  ART_TARGET_CODEGEN_ARCHS += mips
-endif
-ifneq (,$(filter x86_64,$(ART_TARGET_CODEGEN_ARCHS)))
-  ART_TARGET_CODEGEN_ARCHS += x86
-endif
-ART_TARGET_CODEGEN_ARCHS := $(sort $(ART_TARGET_CODEGEN_ARCHS))
-ifneq (,$(filter arm64,$(ART_HOST_CODEGEN_ARCHS)))
-  ART_HOST_CODEGEN_ARCHS += arm
-endif
-ifneq (,$(filter mips64,$(ART_HOST_CODEGEN_ARCHS)))
-  ART_HOST_CODEGEN_ARCHS += mips
-endif
-ifneq (,$(filter x86_64,$(ART_HOST_CODEGEN_ARCHS)))
-  ART_HOST_CODEGEN_ARCHS += x86
-endif
-ART_HOST_CODEGEN_ARCHS := $(sort $(ART_HOST_CODEGEN_ARCHS))
-
-# Base set of cflags used by target build only
-art_target_cflags := \
-  $(foreach target_arch,$(strip $(ART_TARGET_CODEGEN_ARCHS)), -DART_ENABLE_CODEGEN_$(target_arch))
-# Base set of cflags used by host build only
-art_host_cflags := \
-  $(foreach host_arch,$(strip $(ART_HOST_CODEGEN_ARCHS)), -DART_ENABLE_CODEGEN_$(host_arch))
-
-# Base set of asflags used by all things ART.
-art_asflags :=
-
-# Missing declarations: too many at the moment, as we use "extern" quite a bit.
-#  -Wmissing-declarations \
-
-
-
-ifdef ART_IMT_SIZE
-  art_cflags += -DIMT_SIZE=$(ART_IMT_SIZE)
-else
-  # Default is 43
-  art_cflags += -DIMT_SIZE=43
-endif
-
-ifeq ($(ART_HEAP_POISONING),true)
-  art_cflags += -DART_HEAP_POISONING=1
-  art_asflags += -DART_HEAP_POISONING=1
-endif
-
-#
-# Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
-# The default is BAKER.
-#
-ART_READ_BARRIER_TYPE ?= BAKER
-
-ifeq ($(ART_USE_READ_BARRIER),true)
-  art_cflags += -DART_USE_READ_BARRIER=1
-  art_cflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
-  art_asflags += -DART_USE_READ_BARRIER=1
-  art_asflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
-
-  # Temporarily override -fstack-protector-strong with -fstack-protector to avoid a major
-  # slowdown with the read barrier config. b/26744236.
-  art_cflags += -fstack-protector
-endif
-
-ifeq ($(ART_USE_TLAB),true)
-  art_cflags += -DART_USE_TLAB=1
-endif
-
-# Are additional statically-linked ART host binaries (dex2oats,
-# oatdumps, etc.) getting built?
-ifeq ($(ART_BUILD_HOST_STATIC),true)
-  art_cflags += -DART_BUILD_HOST_STATIC=1
-endif
-
-# Temporary flag allowing to disable recent changes in oat file management.
-ifneq ($(ART_ENABLE_VDEX),false)
-  art_cflags += -DART_ENABLE_VDEX
-endif
-
-# Cflags for non-debug ART and ART tools.
-art_non_debug_cflags := \
-  $(ART_NDEBUG_OPT_FLAG)
-
-# Cflags for debug ART and ART tools.
-art_debug_cflags := \
-  $(ART_DEBUG_OPT_FLAG) \
-  -DDYNAMIC_ANNOTATIONS_ENABLED=1 \
-  -DVIXL_DEBUG \
-  -UNDEBUG
-
-# Assembler flags for non-debug ART and ART tools.
-art_non_debug_asflags :=
-
-# Assembler flags for debug ART and ART tools.
-art_debug_asflags := -UNDEBUG
-
-art_host_non_debug_cflags := $(art_non_debug_cflags)
-art_target_non_debug_cflags := $(art_non_debug_cflags)
-
-###
-# Frame size
-###
-
-# Size of the stack-overflow gap.
-ART_STACK_OVERFLOW_GAP_arm := 8192
-ART_STACK_OVERFLOW_GAP_arm64 := 8192
-ART_STACK_OVERFLOW_GAP_mips := 16384
-ART_STACK_OVERFLOW_GAP_mips64 := 16384
-ART_STACK_OVERFLOW_GAP_x86 := 8192
-ART_STACK_OVERFLOW_GAP_x86_64 := 8192
-ART_COMMON_STACK_OVERFLOW_DEFINES := \
-  -DART_STACK_OVERFLOW_GAP_arm=$(ART_STACK_OVERFLOW_GAP_arm) \
-  -DART_STACK_OVERFLOW_GAP_arm64=$(ART_STACK_OVERFLOW_GAP_arm64) \
-  -DART_STACK_OVERFLOW_GAP_mips=$(ART_STACK_OVERFLOW_GAP_mips) \
-  -DART_STACK_OVERFLOW_GAP_mips64=$(ART_STACK_OVERFLOW_GAP_mips64) \
-  -DART_STACK_OVERFLOW_GAP_x86=$(ART_STACK_OVERFLOW_GAP_x86) \
-  -DART_STACK_OVERFLOW_GAP_x86_64=$(ART_STACK_OVERFLOW_GAP_x86_64) \
-
-# Keep these as small as possible. We have separate values as we have some host vs target
-# specific code (and previously GCC vs Clang).
-ART_HOST_FRAME_SIZE_LIMIT := 1736
-ART_TARGET_FRAME_SIZE_LIMIT := 1736
-
-# Frame size adaptations for instrumented builds.
-ifdef SANITIZE_TARGET
-  ART_TARGET_FRAME_SIZE_LIMIT := 6400
-endif
-
-# Add frame-size checks for non-debug builds.
-ifeq ($(HOST_OS),linux)
-  ifneq ($(ART_COVERAGE),true)
-    ifneq ($(NATIVE_COVERAGE),true)
-      art_host_non_debug_cflags += -Wframe-larger-than=$(ART_HOST_FRAME_SIZE_LIMIT)
-      art_target_non_debug_cflags += -Wframe-larger-than=$(ART_TARGET_FRAME_SIZE_LIMIT)
-    endif
-  endif
-endif
-
-
-ART_HOST_CFLAGS := $(art_cflags)
-ART_TARGET_CFLAGS := $(art_cflags)
-
-ART_HOST_ASFLAGS := $(art_asflags)
-ART_TARGET_ASFLAGS := $(art_asflags)
-
-# Bug: 15446488. We don't omit the frame pointer to work around
-# clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
-ART_HOST_CFLAGS += -fno-omit-frame-pointer
-
 ifndef LIBART_IMG_HOST_BASE_ADDRESS
   $(error LIBART_IMG_HOST_BASE_ADDRESS unset)
 endif
-ART_HOST_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
-ART_HOST_CFLAGS += $(art_host_cflags)
-
-ART_HOST_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_HOST_FRAME_SIZE_LIMIT) \
-                   $(ART_COMMON_STACK_OVERFLOW_DEFINES)
-
 
 ifndef LIBART_IMG_TARGET_BASE_ADDRESS
   $(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
 endif
 
-ART_TARGET_CFLAGS += -DART_TARGET \
-                     -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
-
-ART_TARGET_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_TARGET_FRAME_SIZE_LIMIT) \
-                     $(ART_COMMON_STACK_OVERFLOW_DEFINES)
-
-ifeq ($(ART_TARGET_LINUX),true)
-# Setting ART_TARGET_LINUX to true compiles art/ assuming that the target device
-# will be running linux rather than android.
-ART_TARGET_CFLAGS += -DART_TARGET_LINUX
-else
-# The ART_TARGET_ANDROID macro is passed to target builds, which check
-# against it instead of against __ANDROID__ (which is provided by target
-# toolchains).
-ART_TARGET_CFLAGS += -DART_TARGET_ANDROID
-endif
-
-ART_TARGET_CFLAGS += $(art_target_cflags)
-
-ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags)
-ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags)
-ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags)
-ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
-
-ART_HOST_NON_DEBUG_ASFLAGS := $(art_non_debug_asflags)
-ART_TARGET_NON_DEBUG_ASFLAGS := $(art_non_debug_asflags)
-ART_HOST_DEBUG_ASFLAGS := $(art_debug_asflags)
-ART_TARGET_DEBUG_ASFLAGS := $(art_debug_asflags)
-
-ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
-  LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000
-endif
-ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
-  LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA=0x1000000
-endif
-ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
-ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
-
-ifndef LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA
-  LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA=-0x1000000
-endif
-ifndef LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA
-  LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA=0x1000000
-endif
-ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
-ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
-
-# To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16"
-# ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
-
-# Clear locals now they've served their purpose.
-art_cflags :=
-art_asflags :=
-art_host_cflags :=
-art_target_cflags :=
-art_debug_cflags :=
-art_non_debug_cflags :=
-art_debug_asflags :=
-art_non_debug_asflags :=
-art_host_non_debug_cflags :=
-art_target_non_debug_cflags :=
-art_default_gc_type_cflags :=
-
-ART_TARGET_LDFLAGS :=
-
-# $(1): ndebug_or_debug
-define set-target-local-cflags-vars
-  LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
-  LOCAL_ASFLAGS += $(ART_TARGET_ASFLAGS)
-  LOCAL_LDFLAGS += $(ART_TARGET_LDFLAGS)
-  art_target_cflags_ndebug_or_debug := $(1)
-  ifeq ($$(art_target_cflags_ndebug_or_debug),debug)
-    LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS)
-    LOCAL_ASFLAGS += $(ART_TARGET_DEBUG_ASFLAGS)
-  else
-    LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS)
-    LOCAL_ASFLAGS += $(ART_TARGET_NON_DEBUG_ASFLAGS)
-  endif
-
-  # Clear locally used variables.
-  art_target_cflags_ndebug_or_debug :=
-endef
-
 # Support for disabling certain builds.
 ART_BUILD_TARGET := false
 ART_BUILD_HOST := false
-ART_BUILD_NDEBUG := false
-ART_BUILD_DEBUG := false
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
   ART_BUILD_TARGET := true
-  ART_BUILD_NDEBUG := true
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
   ART_BUILD_TARGET := true
-  ART_BUILD_DEBUG := true
 endif
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   ART_BUILD_HOST := true
-  ART_BUILD_NDEBUG := true
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
   ART_BUILD_HOST := true
-  ART_BUILD_DEBUG := true
 endif
 
 endif # ART_ANDROID_COMMON_BUILD_MK
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 8124ca3..449502c 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -26,10 +26,6 @@
 ART_HOST_TEST_DIR := /tmp/$(USER)/test-art-$(shell echo $$PPID)
 endif
 
-# We need to set a define for the nativetest dir so that common_runtime_test will know the right
-# path. (The problem is being a 32b test on 64b device, which is still located in nativetest64).
-ART_TARGET_CFLAGS += -DART_TARGET_NATIVETEST_DIR=${ART_TARGET_NATIVETEST_DIR}
-
 # List of known broken tests that we won't attempt to execute. The test name must be the full
 # rule name such as test-art-host-oat-optimizing-HelloWorld64.
 ART_TEST_KNOWN_BROKEN :=
diff --git a/build/Android.common_utils.mk b/build/Android.common_utils.mk
deleted file mode 100644
index 8069c3a..0000000
--- a/build/Android.common_utils.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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 ART_ANDROID_COMMON_UTILS_MK
-ART_ANDROID_COMMON_UTILS_MK = true
-
-#
-# Convert a string into an uppercase string.
-#
-# $(1): a string which should be made uppercase
-art-string-to-uppercase = $(shell echo $(1) | tr '[:lower:]' '[:upper:]')
-
-endif # ART_ANDROID_COMMON_UTILS_MK
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index 03791f3..d09f290 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -18,7 +18,7 @@
 
 ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py
 ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
-ART_CPPLINT_FLAGS := --quiet
+ART_CPPLINT_FLAGS := --quiet --root=$(ANDROID_BUILD_TOP)
 ART_CPPLINT_INGORED := \
     runtime/elf.h \
     runtime/openjdkjvmti/jvmti.h
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
deleted file mode 100644
index f38a14d..0000000
--- a/build/Android.executable.mk
+++ /dev/null
@@ -1,251 +0,0 @@
-#
-# 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 art/build/Android.common_build.mk
-
-ART_EXECUTABLES_CFLAGS :=
-
-# $(1): executable ("d" will be appended for debug version, "s" will be appended for static version)
-# $(2): source
-# $(3): extra shared libraries
-# $(4): extra include directories
-# $(5): target or host
-# $(6): ndebug or debug
-# $(7): value for LOCAL_MULTILIB (empty means default)
-# $(8): static or shared (empty means shared, applies only for host)
-define build-art-executable
-  ifneq ($(5),target)
-    ifneq ($(5),host)
-      $$(error expected target or host for argument 5, received $(5))
-    endif
-  endif
-  ifneq ($(6),ndebug)
-    ifneq ($(6),debug)
-      $$(error expected ndebug or debug for argument 6, received $(6))
-    endif
-  endif
-
-  art_executable := $(1)
-  art_source := $(2)
-  art_libraries := $(3)
-  art_c_includes := $(4)
-  art_target_or_host := $(5)
-  art_ndebug_or_debug := $(6)
-  art_multilib := $(7)
-  art_static_or_shared := $(8)
-  art_out_binary_name :=
-
-  include $(CLEAR_VARS)
-  LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
-  LOCAL_MODULE_TAGS := optional
-  LOCAL_SRC_FILES := $$(art_source)
-  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime art/cmdline $$(art_c_includes)
-
-  ifeq ($$(art_static_or_shared),static)
-    LOCAL_STATIC_LIBRARIES += $$(art_libraries)
-  else
-    LOCAL_SHARED_LIBRARIES += $$(art_libraries)
-  endif
-
-  ifeq ($$(art_ndebug_or_debug),ndebug)
-    LOCAL_MODULE := $$(art_executable)
-  else #debug
-    LOCAL_MODULE := $$(art_executable)d
-  endif
-
-  ifeq ($$(art_static_or_shared),static)
-    LOCAL_MODULE := $$(LOCAL_MODULE)s
-  endif
-
-  LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
-  # Mac OS linker doesn't understand --export-dynamic.
-  ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host)
-    LOCAL_LDFLAGS := -Wl,--export-dynamic
-  endif
-
-  ifeq ($$(art_target_or_host),target)
-    LOCAL_CLANG := $(ART_TARGET_CLANG)
-    $(call set-target-local-cflags-vars,$(6))
-    LOCAL_SHARED_LIBRARIES += libdl
-  else # host
-    LOCAL_CLANG := $(ART_HOST_CLANG)
-    LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
-    LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
-    ifeq ($$(art_ndebug_or_debug),debug)
-      LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
-      LOCAL_ASFLAGS += $(ART_HOST_DEBUG_ASFLAGS)
-    else
-      LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
-      LOCAL_ASFLAGS += $(ART_HOST_NON_DEBUG_ASFLAGS)
-    endif
-    LOCAL_LDLIBS += -lpthread -ldl
-    ifeq ($$(art_static_or_shared),static)
-      LOCAL_LDFLAGS += -static
-      # We need this because GC stress mode makes use of _Unwind_GetIP and _Unwind_Backtrace and
-      # the symbols are also defined in libgcc_eh.a(unwind-dw2.o)
-      # TODO: Having this is not ideal as it might obscure errors. Try to get rid of it.
-      LOCAL_LDFLAGS += -z muldefs
-      ifeq ($$(HOST_OS),linux)
-        LOCAL_LDLIBS += -lrt -lncurses -ltinfo
-      endif
-      ifeq ($$(HOST_OS),darwin)
-        LOCAL_LDLIBS += -lncurses -ltinfo
-      endif
-    endif
-
-  endif
-
-  # If dynamically linked add libart by default. Statically linked executables
-  # needs to specify it in art_libraries to ensure proper ordering.
-  ifeq ($$(art_ndebug_or_debug),ndebug)
-    ifneq ($$(art_static_or_shared),static)
-      LOCAL_SHARED_LIBRARIES += libart
-    endif
-  else # debug
-    ifneq ($$(art_static_or_shared),static)
-      LOCAL_SHARED_LIBRARIES += libartd
-    endif
-  endif
-
-  LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
-  LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_utils.mk
-  LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.executable.mk
-
-  ifeq ($$(art_target_or_host),target)
-    LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
-  endif
-
-  ifdef ART_MULTILIB_OVERRIDE_$$(art_target_or_host)
-    art_multilib := $$(ART_MULTILIB_OVERRIDE_$$(art_target_or_host))
-  endif
-
-  LOCAL_MULTILIB := $$(art_multilib)
-  art_out_binary_name := $$(LOCAL_MODULE)
-
-  # If multilib=both (potentially building both 32-bit and 64-bit), need to provide stem.
-  ifeq ($$(art_multilib),both)
-    # Set up a 32-bit/64-bit stem if we are building both binaries.
-    # In this case, the 32-bit binary has an additional 32-bit suffix.
-    LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)32
-    LOCAL_MODULE_STEM_64 := $$(LOCAL_MODULE)
-
-    # Remember the binary names so we can add them to the global art executables list later.
-    art_out_binary_name := $$(LOCAL_MODULE_STEM_32) $$(LOCAL_MODULE_STEM_64)
-
-    # For single-architecture targets, remove any binary name suffixes.
-    ifeq ($$(art_target_or_host),target)
-      ifeq (,$(TARGET_2ND_ARCH))
-        LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
-        art_out_binary_name := $$(LOCAL_MODULE)
-      endif
-    endif
-
-    # For single-architecture hosts, remove any binary name suffixes.
-    ifeq ($$(art_target_or_host),host)
-      ifeq (,$(HOST_2ND_ARCH))
-        LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
-        art_out_binary_name := $$(LOCAL_MODULE)
-      endif
-    endif
-  endif
-
-  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
-
-  ifeq ($$(art_target_or_host),target)
-    include $(BUILD_EXECUTABLE)
-  else # host
-    LOCAL_IS_HOST_MODULE := true
-    include $(BUILD_HOST_EXECUTABLE)
-  endif
-
-  # Clear out local variables now that we're done with them.
-  art_executable :=
-  art_source :=
-  art_libraries :=
-  art_c_includes :=
-  art_target_or_host :=
-  art_ndebug_or_debug :=
-  art_multilib :=
-  art_static_or_shared :=
-  art_out_binary_name :=
-
-endef
-
-#
-# Build many art executables from multiple variations (debug/ndebug, host/target, 32/64bit).
-# By default only either 32-bit or 64-bit is built (but not both -- see multilib arg).
-# All other variations are gated by ANDROID_BUILD_(TARGET|HOST)_[N]DEBUG.
-# The result must be eval-uated.
-#
-# $(1): executable name
-# $(2): source files
-# $(3): library dependencies (common); debug prefix is added on as necessary automatically.
-# $(4): library dependencies (target only)
-# $(5): library dependencies (host only)
-# $(6): extra include directories
-# $(7): multilib (default: empty), valid values: {,32,64,both})
-# $(8): host prefer 32-bit: {true, false} (default: false).  If argument
-#       `multilib` is explicitly set to 64, ignore the "host prefer 32-bit"
-#       setting and only build a 64-bit executable on host.
-define build-art-multi-executable
-  $(foreach debug_flavor,ndebug debug,
-    $(foreach target_flavor,host target,
-      art-multi-binary-name := $(1)
-      art-multi-source-files := $(2)
-      art-multi-lib-dependencies := $(3)
-      art-multi-lib-dependencies-target := $(4)
-      art-multi-lib-dependencies-host := $(5)
-      art-multi-include-extra := $(6)
-      art-multi-multilib := $(7)
-      art-multi-host-prefer-32-bit := $(8)
-
-      # Add either -host or -target specific lib dependencies to the lib dependencies.
-      art-multi-lib-dependencies += $$(art-multi-lib-dependencies-$(target_flavor))
-
-      # Replace libart- prefix with libartd- for debug flavor.
-      ifeq ($(debug_flavor),debug)
-        art-multi-lib-dependencies := $$(subst libart-,libartd-,$$(art-multi-lib-dependencies))
-      endif
-
-      # Build the env guard var name, e.g. ART_BUILD_HOST_NDEBUG.
-      art-multi-env-guard := $$(call art-string-to-uppercase,ART_BUILD_$(target_flavor)_$(debug_flavor))
-
-      ifeq ($(target_flavor),host)
-        ifeq ($$(art-multi-host-prefer-32-bit),true)
-          ifneq ($$(art-multi-multilib),64)
-            art-multi-multilib := 32
-          endif
-        endif
-      endif
-
-      # Build the art executable only if the corresponding env guard was set.
-      ifeq ($$($$(art-multi-env-guard)),true)
-        $$(eval $$(call build-art-executable,$$(art-multi-binary-name),$$(art-multi-source-files),$$(art-multi-lib-dependencies),$$(art-multi-include-extra),$(target_flavor),$(debug_flavor),$$(art-multi-multilib)))
-      endif
-
-      # Clear locals now they've served their purpose.
-      art-multi-binary-name :=
-      art-multi-source-files :=
-      art-multi-lib-dependencies :=
-      art-multi-lib-dependencies-target :=
-      art-multi-lib-dependencies-host :=
-      art-multi-include-extra :=
-      art-multi-multilib :=
-      art-multi-host-prefer-32-bit :=
-      art-multi-env-guard :=
-    )
-  )
-endef
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c70f005..850702a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -27,10 +27,13 @@
   AllFields \
   ExceptionHandle \
   GetMethodSignature \
+  ImageLayoutA \
+  ImageLayoutB \
   Instrumentation \
   Interfaces \
   Lookup \
   Main \
+  MethodTypes \
   MultiDex \
   MultiDexModifiedSecondary \
   MyClass \
@@ -78,12 +81,13 @@
 # Dex file dependencies for each gtest.
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 
-ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
-ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages
+ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
+ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
@@ -223,8 +227,6 @@
     $(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_$(m)))
 endif
 
-ART_TEST_CFLAGS :=
-
 # Variables holding collections of gtest pre-requisits used to run a number of gtests.
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
@@ -572,7 +574,6 @@
 RUNTIME_GTEST_HOST_SRC_FILES :=
 COMPILER_GTEST_TARGET_SRC_FILES :=
 COMPILER_GTEST_HOST_SRC_FILES :=
-ART_TEST_CFLAGS :=
 ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
 ART_TEST_HOST_GTEST_RULES :=
diff --git a/build/art.go b/build/art.go
index ba5521a..b826538 100644
--- a/build/art.go
+++ b/build/art.go
@@ -30,6 +30,9 @@
 	var cflags []string
 	var asflags []string
 
+	opt := envDefault(ctx, "ART_NDEBUG_OPT_FLAG", "-O3")
+	cflags = append(cflags, opt)
+
 	tlab := false
 
 	gcType := envDefault(ctx, "ART_DEFAULT_GC_TYPE", "CMS")
@@ -75,11 +78,20 @@
 	return cflags, asflags
 }
 
+func debugFlags(ctx android.BaseContext) []string {
+	var cflags []string
+
+	opt := envDefault(ctx, "ART_DEBUG_OPT_FLAG", "-O2")
+	cflags = append(cflags, opt)
+
+	return cflags
+}
+
 func deviceFlags(ctx android.BaseContext) []string {
 	var cflags []string
 	deviceFrameSizeLimit := 1736
 	if len(ctx.AConfig().SanitizeDevice()) > 0 {
-		deviceFrameSizeLimit = 6400
+		deviceFrameSizeLimit = 7400
 	}
 	cflags = append(cflags,
 		fmt.Sprintf("-Wframe-larger-than=%d", deviceFrameSizeLimit),
@@ -143,6 +155,16 @@
 	ctx.AppendProperties(p)
 }
 
+func debugDefaults(ctx android.LoadHookContext) {
+	type props struct {
+		Cflags []string
+	}
+
+	p := &props{}
+	p.Cflags = debugFlags(ctx)
+	ctx.AppendProperties(p)
+}
+
 func customLinker(ctx android.LoadHookContext) {
 	linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
 	if linker != "" {
@@ -206,6 +228,7 @@
 	soong.RegisterModuleType("art_cc_test_library", artTestLibrary)
 	soong.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
 	soong.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
+	soong.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
 }
 
 func artGlobalDefaultsFactory() (blueprint.Module, []interface{}) {
@@ -215,6 +238,13 @@
 	return module, props
 }
 
+func artDebugDefaultsFactory() (blueprint.Module, []interface{}) {
+	module, props := artDefaultsFactory()
+	android.AddLoadHook(module, debugDefaults)
+
+	return module, props
+}
+
 func artDefaultsFactory() (blueprint.Module, []interface{}) {
 	c := &codegenProperties{}
 	module, props := cc.DefaultsFactory(c)
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index b74e588..b229be4 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -775,6 +775,8 @@
       existing = existing | ExperimentalFlags::kAgents;
     } else if (option == "runtime-plugins") {
       existing = existing | ExperimentalFlags::kRuntimePlugins;
+    } else if (option == "method-handles") {
+      existing = existing | ExperimentalFlags::kMethodHandles;
     } else {
       return Result::Failure(std::string("Unknown option '") + option + "'");
     }
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 8a2c94a..7fb009a 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -31,8 +31,6 @@
         "dex/verified_method.cc",
         "dex/verification_results.cc",
         "dex/quick_compiler_callbacks.cc",
-        "dex/quick/dex_file_method_inliner.cc",
-        "dex/quick/dex_file_to_method_inliner_map.cc",
         "driver/compiled_method_storage.cc",
         "driver/compiler_driver.cc",
         "driver/compiler_options.cc",
@@ -100,6 +98,7 @@
                 "linker/arm/relative_patcher_arm_base.cc",
                 "linker/arm/relative_patcher_thumb2.cc",
                 "optimizing/code_generator_arm.cc",
+                "optimizing/code_generator_arm_vixl.cc",
                 "optimizing/dex_cache_array_fixups_arm.cc",
                 "optimizing/instruction_simplifier_arm.cc",
                 "optimizing/instruction_simplifier_shared.cc",
@@ -184,6 +183,7 @@
     },
     generated_sources: ["art_compiler_operator_srcs"],
     shared_libs: [
+        "libbase",
         "liblz4",
         "liblzma",
     ],
@@ -287,6 +287,7 @@
     shared_libs: [
         "libartd-compiler",
         "libart-runtime-gtest",
+        "libbase",
     ],
 }
 
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 06a39b2..b726649 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -23,7 +23,6 @@
 #include "class_linker.h"
 #include "compiled_method.h"
 #include "dex/quick_compiler_callbacks.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
@@ -33,7 +32,7 @@
 #include "mirror/dex_cache.h"
 #include "mirror/object-inl.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
 
@@ -109,7 +108,8 @@
   FlushInstructionCache(reinterpret_cast<char*>(base), reinterpret_cast<char*>(base + len));
 }
 
-void CommonCompilerTest::MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name) {
+void CommonCompilerTest::MakeExecutable(ObjPtr<mirror::ClassLoader> class_loader,
+                                        const char* class_name) {
   std::string class_descriptor(DotToDescriptor(class_name));
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
@@ -176,7 +176,6 @@
                                               size_t number_of_threads) {
   compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                             verification_results_.get(),
-                                            method_inliner_map_.get(),
                                             kind,
                                             isa,
                                             instruction_set_features_.get(),
@@ -200,9 +199,7 @@
 
   compiler_options_.reset(new CompilerOptions);
   verification_results_.reset(new VerificationResults(compiler_options_.get()));
-  method_inliner_map_.reset(new DexFileToMethodInlinerMap);
   callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
-                                              method_inliner_map_.get(),
                                               CompilerCallbacks::CallbackMode::kCompileApp));
 }
 
@@ -223,9 +220,9 @@
   timer_.reset();
   compiler_driver_.reset();
   callbacks_.reset();
-  method_inliner_map_.reset();
   verification_results_.reset();
   compiler_options_.reset();
+  image_reservation_.reset();
 
   CommonRuntimeTest::TearDown();
 }
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 5239121..f4838c1 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -34,7 +34,6 @@
 class CompilerDriver;
 class CompilerOptions;
 class CumulativeLogger;
-class DexFileToMethodInlinerMap;
 class VerificationResults;
 
 template<class T> class Handle;
@@ -51,7 +50,7 @@
 
   static void MakeExecutable(const void* code_start, size_t code_length);
 
-  void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name)
+  void MakeExecutable(ObjPtr<mirror::ClassLoader> class_loader, const char* class_name)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  protected:
@@ -102,7 +101,6 @@
   Compiler::Kind compiler_kind_ = Compiler::kOptimizing;
   std::unique_ptr<CompilerOptions> compiler_options_;
   std::unique_ptr<VerificationResults> verification_results_;
-  std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_;
   std::unique_ptr<CompilerDriver> compiler_driver_;
   std::unique_ptr<CumulativeLogger> timer_;
   std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 3ce786e..236a3b2 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -90,7 +90,7 @@
 
   // Compiles a virtual method invocation into a quick virtual method invocation.
   // The method index is replaced by the vtable index where the corresponding
-  // AbstractMethod can be found. Therefore, this does not involve any resolution
+  // Executable can be found. Therefore, this does not involve any resolution
   // at runtime.
   // Since the method index is encoded with 16 bits, we can replace it only if the
   // vtable index can be encoded with 16 bits too.
@@ -277,39 +277,44 @@
     return;
   }
   uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-  MethodReference target_method(&GetDexFile(), method_idx);
-  InvokeType invoke_type = kVirtual;
-  InvokeType original_invoke_type = invoke_type;
-  int vtable_idx;
-  uintptr_t direct_code;
-  uintptr_t direct_method;
-  // TODO: support devirtualization.
-  const bool kEnableDevirtualization = false;
-  bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc,
-                                             false, kEnableDevirtualization,
-                                             &invoke_type,
-                                             &target_method, &vtable_idx,
-                                             &direct_code, &direct_method);
-  if (fast_path && original_invoke_type == invoke_type) {
-    if (vtable_idx >= 0 && IsUint<16>(vtable_idx)) {
-      VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
-                     << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
-                     << " to " << Instruction::Name(new_opcode)
-                     << " by replacing method index " << method_idx
-                     << " by vtable index " << vtable_idx
-                     << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
-                     << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
-      // We are modifying 4 consecutive bytes.
-      inst->SetOpcode(new_opcode);
-      // Replace method index by vtable index.
-      if (is_range) {
-        inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
-      } else {
-        inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
-      }
-      quickened_info_.push_back(QuickenedInfo(dex_pc, method_idx));
-    }
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader())));
+
+  ClassLinker* class_linker = unit_.GetClassLinker();
+  ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
+      GetDexFile(),
+      method_idx,
+      unit_.GetDexCache(),
+      class_loader,
+      /* referrer */ nullptr,
+      kVirtual);
+
+  if (UNLIKELY(resolved_method == nullptr)) {
+    // Clean up any exception left by type resolution.
+    soa.Self()->ClearException();
+    return;
   }
+
+  uint32_t vtable_idx = resolved_method->GetMethodIndex();
+  DCHECK(IsUint<16>(vtable_idx));
+  VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
+                 << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+                 << " to " << Instruction::Name(new_opcode)
+                 << " by replacing method index " << method_idx
+                 << " by vtable index " << vtable_idx
+                 << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                 << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+  // We are modifying 4 consecutive bytes.
+  inst->SetOpcode(new_opcode);
+  // Replace method index by vtable index.
+  if (is_range) {
+    inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
+  } else {
+    inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+  }
+  quickened_info_.push_back(QuickenedInfo(dex_pc, method_idx));
 }
 
 CompiledMethod* ArtCompileDEX(
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
deleted file mode 100644
index 8d53dbf..0000000
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * 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.
- */
-
-#include "dex_file_method_inliner.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/mutex-inl.h"
-#include "driver/compiler_driver.h"
-#include "thread-inl.h"
-#include "dex_instruction-inl.h"
-#include "driver/dex_compilation_unit.h"
-#include "verifier/method_verifier-inl.h"
-
-namespace art {
-
-namespace {  // anonymous namespace
-
-static constexpr bool kIntrinsicIsStatic[] = {
-    true,   // kIntrinsicDoubleCvt
-    true,   // kIntrinsicFloatCvt
-    true,   // kIntrinsicFloat2Int
-    true,   // kIntrinsicDouble2Long
-    true,   // kIntrinsicFloatIsInfinite
-    true,   // kIntrinsicDoubleIsInfinite
-    true,   // kIntrinsicFloatIsNaN
-    true,   // kIntrinsicDoubleIsNaN
-    true,   // kIntrinsicReverseBits
-    true,   // kIntrinsicReverseBytes
-    true,   // kIntrinsicBitCount
-    true,   // kIntrinsicCompare,
-    true,   // kIntrinsicHighestOneBit
-    true,   // kIntrinsicLowestOneBit
-    true,   // kIntrinsicNumberOfLeadingZeros
-    true,   // kIntrinsicNumberOfTrailingZeros
-    true,   // kIntrinsicRotateRight
-    true,   // kIntrinsicRotateLeft
-    true,   // kIntrinsicSignum
-    true,   // kIntrinsicAbsInt
-    true,   // kIntrinsicAbsLong
-    true,   // kIntrinsicAbsFloat
-    true,   // kIntrinsicAbsDouble
-    true,   // kIntrinsicMinMaxInt
-    true,   // kIntrinsicMinMaxLong
-    true,   // kIntrinsicMinMaxFloat
-    true,   // kIntrinsicMinMaxDouble
-    true,   // kIntrinsicCos
-    true,   // kIntrinsicSin
-    true,   // kIntrinsicAcos
-    true,   // kIntrinsicAsin
-    true,   // kIntrinsicAtan
-    true,   // kIntrinsicAtan2
-    true,   // kIntrinsicCbrt
-    true,   // kIntrinsicCosh
-    true,   // kIntrinsicExp
-    true,   // kIntrinsicExpm1
-    true,   // kIntrinsicHypot
-    true,   // kIntrinsicLog
-    true,   // kIntrinsicLog10
-    true,   // kIntrinsicNextAfter
-    true,   // kIntrinsicSinh
-    true,   // kIntrinsicTan
-    true,   // kIntrinsicTanh
-    true,   // kIntrinsicSqrt
-    true,   // kIntrinsicCeil
-    true,   // kIntrinsicFloor
-    true,   // kIntrinsicRint
-    true,   // kIntrinsicRoundFloat
-    true,   // kIntrinsicRoundDouble
-    false,  // kIntrinsicReferenceGetReferent
-    false,  // kIntrinsicCharAt
-    false,  // kIntrinsicCompareTo
-    false,  // kIntrinsicEquals
-    false,  // kIntrinsicGetCharsNoCheck
-    false,  // kIntrinsicIsEmptyOrLength
-    false,  // kIntrinsicIndexOf
-    true,   // kIntrinsicNewStringFromBytes
-    true,   // kIntrinsicNewStringFromChars
-    true,   // kIntrinsicNewStringFromString
-    true,   // kIntrinsicCurrentThread
-    true,   // kIntrinsicPeek
-    true,   // kIntrinsicPoke
-    false,  // kIntrinsicCas
-    false,  // kIntrinsicUnsafeGet
-    false,  // kIntrinsicUnsafePut
-    false,  // kIntrinsicUnsafeGetAndAddInt,
-    false,  // kIntrinsicUnsafeGetAndAddLong,
-    false,  // kIntrinsicUnsafeGetAndSetInt,
-    false,  // kIntrinsicUnsafeGetAndSetLong,
-    false,  // kIntrinsicUnsafeGetAndSetObject,
-    false,  // kIntrinsicUnsafeLoadFence,
-    false,  // kIntrinsicUnsafeStoreFence,
-    false,  // kIntrinsicUnsafeFullFence,
-    true,   // kIntrinsicSystemArrayCopyCharArray
-    true,   // kIntrinsicSystemArrayCopy
-};
-static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop,
-              "arraysize of kIntrinsicIsStatic unexpected");
-static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsNaN], "DoubleIsNaN must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicReverseBits], "ReverseBits must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicBitCount], "BitCount must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCompare], "Compare must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicHighestOneBit], "HighestOneBit must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicLowestOneBit], "LowestOneBit  must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros],
-              "NumberOfLeadingZeros must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfTrailingZeros],
-              "NumberOfTrailingZeros must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicRotateRight], "RotateRight must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicRotateLeft], "RotateLeft must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSignum], "Signum must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAbsDouble], "AbsDouble must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxInt], "MinMaxInt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCos], "Cos must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSin], "Sin must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAcos], "Acos must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAsin], "Asin must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAtan], "Atan must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicAtan2], "Atan2 must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCbrt], "Cbrt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCosh], "Cosh must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicExp], "Exp must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicExpm1], "Expm1 must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicHypot], "Hypot must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicLog], "Log must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicLog10], "Log10 must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNextAfter], "NextAfter must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSinh], "Sinh must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicTan], "Tan must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicTanh], "Tanh must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSqrt], "Sqrt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCeil], "Ceil must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicFloor], "Floor must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicRint], "Rint must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicRoundFloat], "RoundFloat must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicRoundDouble], "RoundDouble must be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], "Get must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicCharAt], "CharAt must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicCompareTo], "CompareTo must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicEquals], "String equals must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicGetCharsNoCheck], "GetCharsNoCheck must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], "IsEmptyOrLength must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicIndexOf], "IndexOf must not be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromBytes],
-              "NewStringFromBytes must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromChars],
-              "NewStringFromChars must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromString],
-              "NewStringFromString must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicCurrentThread], "CurrentThread must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
-              "SystemArrayCopyCharArray must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy],
-              "SystemArrayCopy must be static");
-
-}  // anonymous namespace
-
-const uint32_t DexFileMethodInliner::kIndexUnresolved;
-const char* const DexFileMethodInliner::kClassCacheNames[] = {
-    "Z",                       // kClassCacheBoolean
-    "B",                       // kClassCacheByte
-    "C",                       // kClassCacheChar
-    "S",                       // kClassCacheShort
-    "I",                       // kClassCacheInt
-    "J",                       // kClassCacheLong
-    "F",                       // kClassCacheFloat
-    "D",                       // kClassCacheDouble
-    "V",                       // kClassCacheVoid
-    "[B",                      // kClassCacheJavaLangByteArray
-    "[C",                      // kClassCacheJavaLangCharArray
-    "[I",                      // kClassCacheJavaLangIntArray
-    "Ljava/lang/Object;",      // kClassCacheJavaLangObject
-    "Ljava/lang/ref/Reference;",   // kClassCacheJavaLangRefReference
-    "Ljava/lang/String;",      // kClassCacheJavaLangString
-    "Ljava/lang/StringBuffer;",    // kClassCacheJavaLangStringBuffer
-    "Ljava/lang/StringBuilder;",   // kClassCacheJavaLangStringBuilder
-    "Ljava/lang/StringFactory;",   // kClassCacheJavaLangStringFactory
-    "Ljava/lang/Double;",      // kClassCacheJavaLangDouble
-    "Ljava/lang/Float;",       // kClassCacheJavaLangFloat
-    "Ljava/lang/Integer;",     // kClassCacheJavaLangInteger
-    "Ljava/lang/Long;",        // kClassCacheJavaLangLong
-    "Ljava/lang/Short;",       // kClassCacheJavaLangShort
-    "Ljava/lang/Math;",        // kClassCacheJavaLangMath
-    "Ljava/lang/StrictMath;",  // kClassCacheJavaLangStrictMath
-    "Ljava/lang/Thread;",      // kClassCacheJavaLangThread
-    "Ljava/nio/charset/Charset;",  // kClassCacheJavaNioCharsetCharset
-    "Llibcore/io/Memory;",     // kClassCacheLibcoreIoMemory
-    "Lsun/misc/Unsafe;",       // kClassCacheSunMiscUnsafe
-    "Ljava/lang/System;",      // kClassCacheJavaLangSystem
-};
-
-const char* const DexFileMethodInliner::kNameCacheNames[] = {
-    "reverse",               // kNameCacheReverse
-    "reverseBytes",          // kNameCacheReverseBytes
-    "doubleToRawLongBits",   // kNameCacheDoubleToRawLongBits
-    "longBitsToDouble",      // kNameCacheLongBitsToDouble
-    "floatToRawIntBits",     // kNameCacheFloatToRawIntBits
-    "intBitsToFloat",        // kNameCacheIntBitsToFloat
-    "abs",                   // kNameCacheAbs
-    "max",                   // kNameCacheMax
-    "min",                   // kNameCacheMin
-    "cos",                   // kNameCacheCos
-    "sin",                   // kNameCacheSin
-    "acos",                  // kNameCacheAcos
-    "asin",                  // kNameCacheAsin
-    "atan",                  // kNameCacheAtan
-    "atan2",                 // kNameCacheAtan2
-    "cbrt",                  // kNameCacheCbrt
-    "cosh",                  // kNameCacheCosh
-    "exp",                   // kNameCacheExp
-    "expm1",                 // kNameCacheExpm1
-    "hypot",                 // kNameCacheHypot
-    "log",                   // kNameCacheLog
-    "log10",                 // kNameCacheLog10
-    "nextAfter",             // kNameCacheNextAfter
-    "sinh",                  // kNameCacheSinh
-    "tan",                   // kNameCacheTan
-    "tanh",                  // kNameCacheTanh
-    "sqrt",                  // kNameCacheSqrt
-    "ceil",                  // kNameCacheCeil
-    "floor",                 // kNameCacheFloor
-    "rint",                  // kNameCacheRint
-    "round",                 // kNameCacheRound
-    "getReferent",           // kNameCacheReferenceGet
-    "charAt",                // kNameCacheCharAt
-    "compareTo",             // kNameCacheCompareTo
-    "equals",                // kNameCacheEquals
-    "getCharsNoCheck",       // kNameCacheGetCharsNoCheck
-    "isEmpty",               // kNameCacheIsEmpty
-    "floatToIntBits",        // kNameCacheFloatToIntBits
-    "doubleToLongBits",      // kNameCacheDoubleToLongBits
-    "isInfinite",            // kNameCacheIsInfinite
-    "isNaN",                 // kNameCacheIsNaN
-    "indexOf",               // kNameCacheIndexOf
-    "length",                // kNameCacheLength
-    "<init>",                // kNameCacheInit
-    "newStringFromBytes",    // kNameCacheNewStringFromBytes
-    "newStringFromChars",    // kNameCacheNewStringFromChars
-    "newStringFromString",   // kNameCacheNewStringFromString
-    "currentThread",         // kNameCacheCurrentThread
-    "peekByte",              // kNameCachePeekByte
-    "peekIntNative",         // kNameCachePeekIntNative
-    "peekLongNative",        // kNameCachePeekLongNative
-    "peekShortNative",       // kNameCachePeekShortNative
-    "pokeByte",              // kNameCachePokeByte
-    "pokeIntNative",         // kNameCachePokeIntNative
-    "pokeLongNative",        // kNameCachePokeLongNative
-    "pokeShortNative",       // kNameCachePokeShortNative
-    "compareAndSwapInt",     // kNameCacheCompareAndSwapInt
-    "compareAndSwapLong",    // kNameCacheCompareAndSwapLong
-    "compareAndSwapObject",  // kNameCacheCompareAndSwapObject
-    "getInt",                // kNameCacheGetInt
-    "getIntVolatile",        // kNameCacheGetIntVolatile
-    "putInt",                // kNameCachePutInt
-    "putIntVolatile",        // kNameCachePutIntVolatile
-    "putOrderedInt",         // kNameCachePutOrderedInt
-    "getLong",               // kNameCacheGetLong
-    "getLongVolatile",       // kNameCacheGetLongVolatile
-    "putLong",               // kNameCachePutLong
-    "putLongVolatile",       // kNameCachePutLongVolatile
-    "putOrderedLong",        // kNameCachePutOrderedLong
-    "getObject",             // kNameCacheGetObject
-    "getObjectVolatile",     // kNameCacheGetObjectVolatile
-    "putObject",             // kNameCachePutObject
-    "putObjectVolatile",     // kNameCachePutObjectVolatile
-    "putOrderedObject",      // kNameCachePutOrderedObject
-    "getAndAddInt",          // kNameCacheGetAndAddInt,
-    "getAndAddLong",         // kNameCacheGetAndAddLong,
-    "getAndSetInt",          // kNameCacheGetAndSetInt,
-    "getAndSetLong",         // kNameCacheGetAndSetLong,
-    "getAndSetObject",       // kNameCacheGetAndSetObject,
-    "loadFence",             // kNameCacheLoadFence,
-    "storeFence",            // kNameCacheStoreFence,
-    "fullFence",             // kNameCacheFullFence,
-    "arraycopy",             // kNameCacheArrayCopy
-    "bitCount",              // kNameCacheBitCount
-    "compare",               // kNameCacheCompare
-    "highestOneBit",         // kNameCacheHighestOneBit
-    "lowestOneBit",          // kNameCacheLowestOneBit
-    "numberOfLeadingZeros",  // kNameCacheNumberOfLeadingZeros
-    "numberOfTrailingZeros",  // kNameCacheNumberOfTrailingZeros
-    "rotateRight",           // kNameCacheRotateRight
-    "rotateLeft",            // kNameCacheRotateLeft
-    "signum",                // kNameCacheSignum
-};
-
-const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
-    // kProtoCacheI_I
-    { kClassCacheInt, 1, { kClassCacheInt } },
-    // kProtoCacheJ_J
-    { kClassCacheLong, 1, { kClassCacheLong } },
-    // kProtoCacheS_S
-    { kClassCacheShort, 1, { kClassCacheShort } },
-    // kProtoCacheD_D
-    { kClassCacheDouble, 1, { kClassCacheDouble } },
-    // kProtoCacheDD_D
-    { kClassCacheDouble, 2, { kClassCacheDouble, kClassCacheDouble } },
-    // kProtoCacheF_F
-    { kClassCacheFloat, 1, { kClassCacheFloat } },
-    // kProtoCacheFF_F
-    { kClassCacheFloat, 2, { kClassCacheFloat, kClassCacheFloat } },
-    // kProtoCacheD_J
-    { kClassCacheLong, 1, { kClassCacheDouble } },
-    // kProtoCacheD_Z
-    { kClassCacheBoolean, 1, { kClassCacheDouble } },
-    // kProtoCacheJ_D
-    { kClassCacheDouble, 1, { kClassCacheLong } },
-    // kProtoCacheF_I
-    { kClassCacheInt, 1, { kClassCacheFloat } },
-    // kProtoCacheF_Z
-    { kClassCacheBoolean, 1, { kClassCacheFloat } },
-    // kProtoCacheI_F
-    { kClassCacheFloat, 1, { kClassCacheInt } },
-    // kProtoCacheII_I
-    { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } },
-    // kProtoCacheI_C
-    { kClassCacheChar, 1, { kClassCacheInt } },
-    // kProtoCacheString_I
-    { kClassCacheInt, 1, { kClassCacheJavaLangString } },
-    // kProtoCache_Z
-    { kClassCacheBoolean, 0, { } },
-    // kProtoCache_I
-    { kClassCacheInt, 0, { } },
-    // kProtoCache_Object
-    { kClassCacheJavaLangObject, 0, { } },
-    // kProtoCache_Thread
-    { kClassCacheJavaLangThread, 0, { } },
-    // kProtoCacheJ_B
-    { kClassCacheByte, 1, { kClassCacheLong } },
-    // kProtoCacheJ_I
-    { kClassCacheInt, 1, { kClassCacheLong } },
-    // kProtoCacheJ_S
-    { kClassCacheShort, 1, { kClassCacheLong } },
-    // kProtoCacheJB_V
-    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } },
-    // kProtoCacheJI_V
-    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } },
-    // kProtoCacheJJ_J
-    { kClassCacheLong, 2, { kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheJJ_I
-    { kClassCacheInt, 2, { kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheJJ_V
-    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheJS_V
-    { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
-    // kProtoCacheObject_Z
-    { kClassCacheBoolean, 1, { kClassCacheJavaLangObject } },
-    // kProtoCacheJI_J
-    { kClassCacheLong, 2, { kClassCacheLong, kClassCacheInt } },
-    // kProtoCacheObjectJII_Z
-    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
-        kClassCacheInt, kClassCacheInt } },
-    // kProtoCacheObjectJJJ_Z
-    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
-        kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheObjectJObjectObject_Z
-    { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
-        kClassCacheJavaLangObject, kClassCacheJavaLangObject } },
-    // kProtoCacheObjectJ_I
-    { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
-    // kProtoCacheObjectJI_I
-    { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
-    // kProtoCacheObjectJI_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } },
-    // kProtoCacheObjectJ_J
-    { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
-    // kProtoCacheObjectJJ_J
-    { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheObjectJJ_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } },
-    // kProtoCacheObjectJ_Object
-    { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } },
-    // kProtoCacheObjectJObject_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong,
-        kClassCacheJavaLangObject } },
-    // kProtoCacheObjectJObject_Object
-    { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong,
-        kClassCacheJavaLangObject } },
-    // kProtoCacheCharArrayICharArrayII_V
-    { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt,
-        kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} },
-    // kProtoCacheObjectIObjectII_V
-    { kClassCacheVoid, 5, {kClassCacheJavaLangObject, kClassCacheInt,
-        kClassCacheJavaLangObject, kClassCacheInt, kClassCacheInt} },
-    // kProtoCacheIICharArrayI_V
-    { kClassCacheVoid, 4, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray,
-        kClassCacheInt } },
-    // kProtoCacheByteArrayIII_String
-    { kClassCacheJavaLangString, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
-        kClassCacheInt } },
-    // kProtoCacheIICharArray_String
-    { kClassCacheJavaLangString, 3, { kClassCacheInt, kClassCacheInt,
-        kClassCacheJavaLangCharArray } },
-    // kProtoCacheString_String
-    { kClassCacheJavaLangString, 1, { kClassCacheJavaLangString } },
-    // kProtoCache_V
-    { kClassCacheVoid, 0, { } },
-    // kProtoCacheByteArray_V
-    { kClassCacheVoid, 1, { kClassCacheJavaLangByteArray } },
-    // kProtoCacheByteArrayI_V
-    { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheInt } },
-    // kProtoCacheByteArrayII_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt } },
-    // kProtoCacheByteArrayIII_V
-    { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
-        kClassCacheInt } },
-    // kProtoCacheByteArrayIIString_V
-    { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
-        kClassCacheJavaLangString } },
-    // kProtoCacheByteArrayString_V
-    { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaLangString } },
-    // kProtoCacheByteArrayIICharset_V
-    { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt,
-        kClassCacheJavaNioCharsetCharset } },
-    // kProtoCacheByteArrayCharset_V
-    { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaNioCharsetCharset } },
-    // kProtoCacheCharArray_V
-    { kClassCacheVoid, 1, { kClassCacheJavaLangCharArray } },
-    // kProtoCacheCharArrayII_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt } },
-    // kProtoCacheIICharArray_V
-    { kClassCacheVoid, 3, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray } },
-    // kProtoCacheIntArrayII_V
-    { kClassCacheVoid, 3, { kClassCacheJavaLangIntArray, kClassCacheInt, kClassCacheInt } },
-    // kProtoCacheString_V
-    { kClassCacheVoid, 1, { kClassCacheJavaLangString } },
-    // kProtoCacheStringBuffer_V
-    { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuffer } },
-    // kProtoCacheStringBuilder_V
-    { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuilder } },
-};
-
-const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
-#define INTRINSIC(c, n, p, o, d) \
-    { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } }
-
-    INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
-    INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, kIntrinsicFlagToFloatingPoint),
-    INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
-    INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint),
-
-    INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0),
-    INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0),
-
-    INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0),
-    INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0),
-    INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0),
-    INTRINSIC(JavaLangDouble, IsNaN, D_Z, kIntrinsicDoubleIsNaN, 0),
-
-    INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
-    INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
-    INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf),
-    INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32),
-    INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64),
-
-    INTRINSIC(JavaLangInteger, BitCount, I_I, kIntrinsicBitCount, k32),
-    INTRINSIC(JavaLangLong, BitCount, J_I, kIntrinsicBitCount, k64),
-    INTRINSIC(JavaLangInteger, Compare, II_I, kIntrinsicCompare, k32),
-    INTRINSIC(JavaLangLong, Compare, JJ_I, kIntrinsicCompare, k64),
-    INTRINSIC(JavaLangInteger, HighestOneBit, I_I, kIntrinsicHighestOneBit, k32),
-    INTRINSIC(JavaLangLong, HighestOneBit, J_J, kIntrinsicHighestOneBit, k64),
-    INTRINSIC(JavaLangInteger, LowestOneBit, I_I, kIntrinsicLowestOneBit, k32),
-    INTRINSIC(JavaLangLong, LowestOneBit, J_J, kIntrinsicLowestOneBit, k64),
-    INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32),
-    INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64),
-    INTRINSIC(JavaLangInteger, NumberOfTrailingZeros, I_I, kIntrinsicNumberOfTrailingZeros, k32),
-    INTRINSIC(JavaLangLong, NumberOfTrailingZeros, J_I, kIntrinsicNumberOfTrailingZeros, k64),
-    INTRINSIC(JavaLangInteger, Signum, I_I, kIntrinsicSignum, k32),
-    INTRINSIC(JavaLangLong, Signum, J_I, kIntrinsicSignum, k64),
-
-    INTRINSIC(JavaLangMath,       Abs, I_I, kIntrinsicAbsInt, 0),
-    INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
-    INTRINSIC(JavaLangMath,       Abs, J_J, kIntrinsicAbsLong, 0),
-    INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0),
-    INTRINSIC(JavaLangMath,       Abs, F_F, kIntrinsicAbsFloat, 0),
-    INTRINSIC(JavaLangStrictMath, Abs, F_F, kIntrinsicAbsFloat, 0),
-    INTRINSIC(JavaLangMath,       Abs, D_D, kIntrinsicAbsDouble, 0),
-    INTRINSIC(JavaLangStrictMath, Abs, D_D, kIntrinsicAbsDouble, 0),
-    INTRINSIC(JavaLangMath,       Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangMath,       Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangMath,       Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangStrictMath, Min, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangMath,       Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangStrictMath, Max, JJ_J, kIntrinsicMinMaxLong, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangMath,       Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangStrictMath, Min, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangMath,       Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangStrictMath, Max, FF_F, kIntrinsicMinMaxFloat, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangMath,       Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangStrictMath, Min, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMin),
-    INTRINSIC(JavaLangMath,       Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
-    INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
-
-    INTRINSIC(JavaLangMath,       Cos, D_D, kIntrinsicCos, 0),
-    INTRINSIC(JavaLangMath,       Sin, D_D, kIntrinsicSin, 0),
-    INTRINSIC(JavaLangMath,       Acos, D_D, kIntrinsicAcos, 0),
-    INTRINSIC(JavaLangMath,       Asin, D_D, kIntrinsicAsin, 0),
-    INTRINSIC(JavaLangMath,       Atan, D_D, kIntrinsicAtan, 0),
-    INTRINSIC(JavaLangMath,       Atan2, DD_D, kIntrinsicAtan2, 0),
-    INTRINSIC(JavaLangMath,       Cbrt, D_D, kIntrinsicCbrt, 0),
-    INTRINSIC(JavaLangMath,       Cosh, D_D, kIntrinsicCosh, 0),
-    INTRINSIC(JavaLangMath,       Exp, D_D, kIntrinsicExp, 0),
-    INTRINSIC(JavaLangMath,       Expm1, D_D, kIntrinsicExpm1, 0),
-    INTRINSIC(JavaLangMath,       Hypot, DD_D, kIntrinsicHypot, 0),
-    INTRINSIC(JavaLangMath,       Log, D_D, kIntrinsicLog, 0),
-    INTRINSIC(JavaLangMath,       Log10, D_D, kIntrinsicLog10, 0),
-    INTRINSIC(JavaLangMath,       NextAfter, DD_D, kIntrinsicNextAfter, 0),
-    INTRINSIC(JavaLangMath,       Sinh, D_D, kIntrinsicSinh, 0),
-    INTRINSIC(JavaLangMath,       Tan, D_D, kIntrinsicTan, 0),
-    INTRINSIC(JavaLangMath,       Tanh, D_D, kIntrinsicTanh, 0),
-    INTRINSIC(JavaLangMath,       Sqrt, D_D, kIntrinsicSqrt, 0),
-    INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0),
-
-    INTRINSIC(JavaLangMath,       Ceil, D_D, kIntrinsicCeil, 0),
-    INTRINSIC(JavaLangStrictMath, Ceil, D_D, kIntrinsicCeil, 0),
-    INTRINSIC(JavaLangMath,       Floor, D_D, kIntrinsicFloor, 0),
-    INTRINSIC(JavaLangStrictMath, Floor, D_D, kIntrinsicFloor, 0),
-    INTRINSIC(JavaLangMath,       Rint, D_D, kIntrinsicRint, 0),
-    INTRINSIC(JavaLangStrictMath, Rint, D_D, kIntrinsicRint, 0),
-    INTRINSIC(JavaLangMath,       Round, F_I, kIntrinsicRoundFloat, 0),
-    INTRINSIC(JavaLangStrictMath, Round, F_I, kIntrinsicRoundFloat, 0),
-    INTRINSIC(JavaLangMath,       Round, D_J, kIntrinsicRoundDouble, 0),
-    INTRINSIC(JavaLangStrictMath, Round, D_J, kIntrinsicRoundDouble, 0),
-
-    INTRINSIC(JavaLangRefReference, ReferenceGetReferent, _Object, kIntrinsicReferenceGetReferent, 0),
-
-    INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0),
-    INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0),
-    INTRINSIC(JavaLangString, Equals, Object_Z, kIntrinsicEquals, 0),
-    INTRINSIC(JavaLangString, GetCharsNoCheck, IICharArrayI_V, kIntrinsicGetCharsNoCheck, 0),
-    INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty),
-    INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone),
-    INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0),
-    INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength),
-
-    INTRINSIC(JavaLangStringFactory, NewStringFromBytes, ByteArrayIII_String,
-              kIntrinsicNewStringFromBytes, kIntrinsicFlagNone),
-    INTRINSIC(JavaLangStringFactory, NewStringFromChars, IICharArray_String,
-              kIntrinsicNewStringFromChars, kIntrinsicFlagNone),
-    INTRINSIC(JavaLangStringFactory, NewStringFromString, String_String,
-              kIntrinsicNewStringFromString, kIntrinsicFlagNone),
-
-    INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0),
-
-    INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte),
-    INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, k32),
-    INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, k64),
-    INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf),
-    INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte),
-    INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, k32),
-    INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, k64),
-    INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf),
-
-    INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas,
-              kIntrinsicFlagNone),
-    INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas,
-              kIntrinsicFlagIsLong),
-    INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas,
-              kIntrinsicFlagIsObject),
-
-#define UNSAFE_GET_PUT(type, code, type_flags) \
-    INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
-              type_flags), \
-    INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \
-              (type_flags) | kIntrinsicFlagIsVolatile), \
-    INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
-              type_flags), \
-    INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
-              (type_flags) | kIntrinsicFlagIsVolatile), \
-    INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \
-              (type_flags) | kIntrinsicFlagIsOrdered)
-
-    UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone),
-    UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong),
-    UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject),
-#undef UNSAFE_GET_PUT
-
-    // 1.8
-    INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0),
-    INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0),
-    INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0),
-    INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0),
-    INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0),
-    INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0),
-    INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0),
-    INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0),
-
-    INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
-              0),
-    INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy,
-              0),
-
-    INTRINSIC(JavaLangInteger, RotateRight, II_I, kIntrinsicRotateRight, k32),
-    INTRINSIC(JavaLangLong, RotateRight, JI_J, kIntrinsicRotateRight, k64),
-    INTRINSIC(JavaLangInteger, RotateLeft, II_I, kIntrinsicRotateLeft, k32),
-    INTRINSIC(JavaLangLong, RotateLeft, JI_J, kIntrinsicRotateLeft, k64),
-
-#undef INTRINSIC
-
-#define SPECIAL(c, n, p, o, d) \
-    { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineSpecial, { d } } }
-
-    SPECIAL(JavaLangString, Init, _V, kInlineStringInit, 0),
-    SPECIAL(JavaLangString, Init, ByteArray_V, kInlineStringInit, 1),
-    SPECIAL(JavaLangString, Init, ByteArrayI_V, kInlineStringInit, 2),
-    SPECIAL(JavaLangString, Init, ByteArrayII_V, kInlineStringInit, 3),
-    SPECIAL(JavaLangString, Init, ByteArrayIII_V, kInlineStringInit, 4),
-    SPECIAL(JavaLangString, Init, ByteArrayIIString_V, kInlineStringInit, 5),
-    SPECIAL(JavaLangString, Init, ByteArrayString_V, kInlineStringInit, 6),
-    SPECIAL(JavaLangString, Init, ByteArrayIICharset_V, kInlineStringInit, 7),
-    SPECIAL(JavaLangString, Init, ByteArrayCharset_V, kInlineStringInit, 8),
-    SPECIAL(JavaLangString, Init, CharArray_V, kInlineStringInit, 9),
-    SPECIAL(JavaLangString, Init, CharArrayII_V, kInlineStringInit, 10),
-    SPECIAL(JavaLangString, Init, IICharArray_V, kInlineStringInit, 11),
-    SPECIAL(JavaLangString, Init, IntArrayII_V, kInlineStringInit, 12),
-    SPECIAL(JavaLangString, Init, String_V, kInlineStringInit, 13),
-    SPECIAL(JavaLangString, Init, StringBuffer_V, kInlineStringInit, 14),
-    SPECIAL(JavaLangString, Init, StringBuilder_V, kInlineStringInit, 15),
-
-#undef SPECIAL
-};
-
-DexFileMethodInliner::DexFileMethodInliner()
-    : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
-      dex_file_(nullptr) {
-  static_assert(kClassCacheFirst == 0, "kClassCacheFirst not 0");
-  static_assert(arraysize(kClassCacheNames) == kClassCacheLast,
-                "bad arraysize for kClassCacheNames");
-  static_assert(kNameCacheFirst == 0, "kNameCacheFirst not 0");
-  static_assert(arraysize(kNameCacheNames) == kNameCacheLast,
-                "bad arraysize for kNameCacheNames");
-  static_assert(kProtoCacheFirst == 0, "kProtoCacheFirst not 0");
-  static_assert(arraysize(kProtoCacheDefs) == kProtoCacheLast,
-                "bad arraysize kProtoCacheNames");
-}
-
-DexFileMethodInliner::~DexFileMethodInliner() {
-}
-
-bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) {
-  InlineMethod method;
-  bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method);
-  return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method);
-}
-
-InlineMethodFlags DexFileMethodInliner::IsIntrinsicOrSpecial(uint32_t method_index) {
-  ReaderMutexLock mu(Thread::Current(), lock_);
-  auto it = inline_methods_.find(method_index);
-  if (it != inline_methods_.end()) {
-    DCHECK_NE(it->second.flags & (kInlineIntrinsic | kInlineSpecial), 0);
-    return it->second.flags;
-  } else {
-    return kNoInlineMethodFlags;
-  }
-}
-
-bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) {
-  ReaderMutexLock mu(Thread::Current(), lock_);
-  auto it = inline_methods_.find(method_index);
-  bool res = (it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0);
-  if (res && intrinsic != nullptr) {
-    *intrinsic = it->second;
-  }
-  return res;
-}
-
-bool DexFileMethodInliner::IsSpecial(uint32_t method_index) {
-  ReaderMutexLock mu(Thread::Current(), lock_);
-  auto it = inline_methods_.find(method_index);
-  return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0;
-}
-
-uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
-                                              ClassCacheIndex index) {
-  uint32_t* class_index = &cache->class_indexes[index];
-  if (*class_index != kIndexUnresolved) {
-    return *class_index;
-  }
-
-  const DexFile::TypeId* type_id = dex_file->FindTypeId(kClassCacheNames[index]);
-  if (type_id == nullptr) {
-    *class_index = kIndexNotFound;
-    return *class_index;
-  }
-  *class_index = dex_file->GetIndexForTypeId(*type_id);
-  return *class_index;
-}
-
-uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache,
-                                             NameCacheIndex index) {
-  uint32_t* name_index = &cache->name_indexes[index];
-  if (*name_index != kIndexUnresolved) {
-    return *name_index;
-  }
-
-  const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]);
-  if (string_id == nullptr) {
-    *name_index = kIndexNotFound;
-    return *name_index;
-  }
-  *name_index = dex_file->GetIndexForStringId(*string_id);
-  return *name_index;
-}
-
-uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache,
-                                              ProtoCacheIndex index) {
-  uint32_t* proto_index = &cache->proto_indexes[index];
-  if (*proto_index != kIndexUnresolved) {
-    return *proto_index;
-  }
-
-  const ProtoDef& proto_def = kProtoCacheDefs[index];
-  uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type);
-  if (return_index == kIndexNotFound) {
-    *proto_index = kIndexNotFound;
-    return *proto_index;
-  }
-  uint16_t return_type = static_cast<uint16_t>(return_index);
-  DCHECK_EQ(static_cast<uint32_t>(return_type), return_index);
-
-  uint32_t signature_length = proto_def.param_count;
-  uint16_t signature_type_idxs[kProtoMaxParams];
-  for (uint32_t i = 0; i != signature_length; ++i) {
-    uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]);
-    if (param_index == kIndexNotFound) {
-      *proto_index = kIndexNotFound;
-      return *proto_index;
-    }
-    signature_type_idxs[i] = static_cast<uint16_t>(param_index);
-    DCHECK_EQ(static_cast<uint32_t>(signature_type_idxs[i]), param_index);
-  }
-
-  const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs,
-                                                           signature_length);
-  if (proto_id == nullptr) {
-    *proto_index = kIndexNotFound;
-    return *proto_index;
-  }
-  *proto_index = dex_file->GetIndexForProtoId(*proto_id);
-  return *proto_index;
-}
-
-uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache,
-                                               const MethodDef& method_def) {
-  uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class);
-  if (declaring_class_index == kIndexNotFound) {
-    return kIndexNotFound;
-  }
-  uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name);
-  if (name_index == kIndexNotFound) {
-    return kIndexNotFound;
-  }
-  uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto);
-  if (proto_index == kIndexNotFound) {
-    return kIndexNotFound;
-  }
-  const DexFile::MethodId* method_id =
-      dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index),
-                             dex_file->GetStringId(name_index),
-                             dex_file->GetProtoId(proto_index));
-  if (method_id == nullptr) {
-    return kIndexNotFound;
-  }
-  return dex_file->GetIndexForMethodId(*method_id);
-}
-
-DexFileMethodInliner::IndexCache::IndexCache() {
-  std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved);
-  std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved);
-  std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved);
-}
-
-void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) {
-  DCHECK(dex_file != nullptr);
-  DCHECK(dex_file_ == nullptr);
-  IndexCache cache;
-  for (const IntrinsicDef& def : kIntrinsicMethods) {
-    uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def);
-    if (method_idx != kIndexNotFound) {
-      DCHECK(inline_methods_.find(method_idx) == inline_methods_.end());
-      inline_methods_.Put(method_idx, def.intrinsic);
-    }
-  }
-  dex_file_ = dex_file;
-}
-
-bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMethod& method) {
-  WriterMutexLock mu(Thread::Current(), lock_);
-  if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) {
-    inline_methods_.Put(method_idx, method);
-    return true;
-  } else {
-    if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") {
-      // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet.
-    } else {
-      LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline";
-    }
-    return false;
-  }
-}
-
-uint32_t DexFileMethodInliner::GetOffsetForStringInit(uint32_t method_index,
-                                                      PointerSize pointer_size) {
-  ReaderMutexLock mu(Thread::Current(), lock_);
-  auto it = inline_methods_.find(method_index);
-  if (it != inline_methods_.end() && (it->second.opcode == kInlineStringInit)) {
-    uint32_t string_init_base_offset = Thread::QuickEntryPointOffsetWithSize(
-              OFFSETOF_MEMBER(QuickEntryPoints, pNewEmptyString), pointer_size);
-    return string_init_base_offset + it->second.d.data * static_cast<size_t>(pointer_size);
-  }
-  return 0;
-}
-
-bool DexFileMethodInliner::IsStringInitMethodIndex(uint32_t method_index) {
-  ReaderMutexLock mu(Thread::Current(), lock_);
-  auto it = inline_methods_.find(method_index);
-  return (it != inline_methods_.end()) && (it->second.opcode == kInlineStringInit);
-}
-
-}  // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
deleted file mode 100644
index 43fc687..0000000
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
-#define ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
-
-#include <stdint.h>
-
-#include "base/enums.h"
-#include "base/mutex.h"
-#include "base/macros.h"
-#include "safe_map.h"
-#include "dex_file.h"
-#include "quick/inline_method_analyser.h"
-
-namespace art {
-
-namespace verifier {
-class MethodVerifier;
-}  // namespace verifier
-
-enum OpSize {
-  k32,
-  k64,
-  kSignedHalf,
-  kSignedByte,
-};
-
-/**
- * Handles inlining of methods from a particular DexFile.
- *
- * Intrinsics are a special case of inline methods. The DexFile indices for
- * all the supported intrinsic methods are looked up once by the FindIntrinsics
- * function and cached by this class for quick lookup by the method index.
- *
- * TODO: Detect short methods (at least getters, setters and empty functions)
- * from the verifier and mark them for inlining. Inline these methods early
- * during compilation to allow further optimizations. Similarly, provide
- * additional information about intrinsics to the early phases of compilation.
- */
-class DexFileMethodInliner {
-  public:
-    DexFileMethodInliner();
-    ~DexFileMethodInliner();
-
-    /**
-     * Analyse method code to determine if the method is a candidate for inlining.
-     * If it is, record its data for later.
-     *
-     * @param verifier the method verifier holding data about the method to analyse.
-     * @return true if the method is a candidate for inlining, false otherwise.
-     */
-    bool AnalyseMethodCode(verifier::MethodVerifier* verifier)
-        REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_);
-
-    /**
-     * Check whether a particular method index corresponds to an intrinsic or special function.
-     */
-    InlineMethodFlags IsIntrinsicOrSpecial(uint32_t method_index) REQUIRES(!lock_);
-
-    /**
-     * Check whether a particular method index corresponds to an intrinsic function.
-     */
-    bool IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) REQUIRES(!lock_);
-
-    /**
-     * Check whether a particular method index corresponds to a special function.
-     */
-    bool IsSpecial(uint32_t method_index) REQUIRES(!lock_);
-
-    /**
-     * Gets the thread pointer entrypoint offset for a string init method index and pointer size.
-     */
-    uint32_t GetOffsetForStringInit(uint32_t method_index, PointerSize pointer_size)
-        REQUIRES(!lock_);
-
-    /**
-     * Check whether a particular method index is a string init.
-     */
-    bool IsStringInitMethodIndex(uint32_t method_index) REQUIRES(!lock_);
-
-    /**
-     * To avoid multiple lookups of a class by its descriptor, we cache its
-     * type index in the IndexCache. These are the indexes into the IndexCache
-     * class_indexes array.
-     */
-    enum ClassCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
-      kClassCacheFirst = 0,
-      kClassCacheBoolean = kClassCacheFirst,
-      kClassCacheByte,
-      kClassCacheChar,
-      kClassCacheShort,
-      kClassCacheInt,
-      kClassCacheLong,
-      kClassCacheFloat,
-      kClassCacheDouble,
-      kClassCacheVoid,
-      kClassCacheJavaLangByteArray,
-      kClassCacheJavaLangCharArray,
-      kClassCacheJavaLangIntArray,
-      kClassCacheJavaLangObject,
-      kClassCacheJavaLangRefReference,
-      kClassCacheJavaLangString,
-      kClassCacheJavaLangStringBuffer,
-      kClassCacheJavaLangStringBuilder,
-      kClassCacheJavaLangStringFactory,
-      kClassCacheJavaLangDouble,
-      kClassCacheJavaLangFloat,
-      kClassCacheJavaLangInteger,
-      kClassCacheJavaLangLong,
-      kClassCacheJavaLangShort,
-      kClassCacheJavaLangMath,
-      kClassCacheJavaLangStrictMath,
-      kClassCacheJavaLangThread,
-      kClassCacheJavaNioCharsetCharset,
-      kClassCacheLibcoreIoMemory,
-      kClassCacheSunMiscUnsafe,
-      kClassCacheJavaLangSystem,
-      kClassCacheLast
-    };
-
-    /**
-     * To avoid multiple lookups of a method name string, we cache its string
-     * index in the IndexCache. These are the indexes into the IndexCache
-     * name_indexes array.
-     */
-    enum NameCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
-      kNameCacheFirst = 0,
-      kNameCacheReverse =  kNameCacheFirst,
-      kNameCacheReverseBytes,
-      kNameCacheDoubleToRawLongBits,
-      kNameCacheLongBitsToDouble,
-      kNameCacheFloatToRawIntBits,
-      kNameCacheIntBitsToFloat,
-      kNameCacheAbs,
-      kNameCacheMax,
-      kNameCacheMin,
-      kNameCacheCos,
-      kNameCacheSin,
-      kNameCacheAcos,
-      kNameCacheAsin,
-      kNameCacheAtan,
-      kNameCacheAtan2,
-      kNameCacheCbrt,
-      kNameCacheCosh,
-      kNameCacheExp,
-      kNameCacheExpm1,
-      kNameCacheHypot,
-      kNameCacheLog,
-      kNameCacheLog10,
-      kNameCacheNextAfter,
-      kNameCacheSinh,
-      kNameCacheTan,
-      kNameCacheTanh,
-      kNameCacheSqrt,
-      kNameCacheCeil,
-      kNameCacheFloor,
-      kNameCacheRint,
-      kNameCacheRound,
-      kNameCacheReferenceGetReferent,
-      kNameCacheCharAt,
-      kNameCacheCompareTo,
-      kNameCacheEquals,
-      kNameCacheGetCharsNoCheck,
-      kNameCacheIsEmpty,
-      kNameCacheFloatToIntBits,
-      kNameCacheDoubleToLongBits,
-      kNameCacheIsInfinite,
-      kNameCacheIsNaN,
-      kNameCacheIndexOf,
-      kNameCacheLength,
-      kNameCacheInit,
-      kNameCacheNewStringFromBytes,
-      kNameCacheNewStringFromChars,
-      kNameCacheNewStringFromString,
-      kNameCacheCurrentThread,
-      kNameCachePeekByte,
-      kNameCachePeekIntNative,
-      kNameCachePeekLongNative,
-      kNameCachePeekShortNative,
-      kNameCachePokeByte,
-      kNameCachePokeIntNative,
-      kNameCachePokeLongNative,
-      kNameCachePokeShortNative,
-      kNameCacheCompareAndSwapInt,
-      kNameCacheCompareAndSwapLong,
-      kNameCacheCompareAndSwapObject,
-      kNameCacheGetInt,
-      kNameCacheGetIntVolatile,
-      kNameCachePutInt,
-      kNameCachePutIntVolatile,
-      kNameCachePutOrderedInt,
-      kNameCacheGetLong,
-      kNameCacheGetLongVolatile,
-      kNameCachePutLong,
-      kNameCachePutLongVolatile,
-      kNameCachePutOrderedLong,
-      kNameCacheGetObject,
-      kNameCacheGetObjectVolatile,
-      kNameCachePutObject,
-      kNameCachePutObjectVolatile,
-      kNameCachePutOrderedObject,
-      kNameCacheGetAndAddInt,
-      kNameCacheGetAndAddLong,
-      kNameCacheGetAndSetInt,
-      kNameCacheGetAndSetLong,
-      kNameCacheGetAndSetObject,
-      kNameCacheLoadFence,
-      kNameCacheStoreFence,
-      kNameCacheFullFence,
-      kNameCacheArrayCopy,
-      kNameCacheBitCount,
-      kNameCacheCompare,
-      kNameCacheHighestOneBit,
-      kNameCacheLowestOneBit,
-      kNameCacheNumberOfLeadingZeros,
-      kNameCacheNumberOfTrailingZeros,
-      kNameCacheRotateRight,
-      kNameCacheRotateLeft,
-      kNameCacheSignum,
-      kNameCacheLast
-    };
-
-    /**
-     * To avoid multiple lookups of a method signature, we cache its proto
-     * index in the IndexCache. These are the indexes into the IndexCache
-     * proto_indexes array.
-     */
-    enum ProtoCacheIndex : uint8_t {  // unit8_t to save space, make larger if needed
-      kProtoCacheFirst = 0,
-      kProtoCacheI_I = kProtoCacheFirst,
-      kProtoCacheJ_J,
-      kProtoCacheS_S,
-      kProtoCacheD_D,
-      kProtoCacheDD_D,
-      kProtoCacheF_F,
-      kProtoCacheFF_F,
-      kProtoCacheD_J,
-      kProtoCacheD_Z,
-      kProtoCacheJ_D,
-      kProtoCacheF_I,
-      kProtoCacheF_Z,
-      kProtoCacheI_F,
-      kProtoCacheII_I,
-      kProtoCacheI_C,
-      kProtoCacheString_I,
-      kProtoCache_Z,
-      kProtoCache_I,
-      kProtoCache_Object,
-      kProtoCache_Thread,
-      kProtoCacheJ_B,
-      kProtoCacheJ_I,
-      kProtoCacheJ_S,
-      kProtoCacheJB_V,
-      kProtoCacheJI_V,
-      kProtoCacheJJ_J,
-      kProtoCacheJJ_I,
-      kProtoCacheJJ_V,
-      kProtoCacheJS_V,
-      kProtoCacheObject_Z,
-      kProtoCacheJI_J,
-      kProtoCacheObjectJII_Z,
-      kProtoCacheObjectJJJ_Z,
-      kProtoCacheObjectJObjectObject_Z,
-      kProtoCacheObjectJ_I,
-      kProtoCacheObjectJI_I,
-      kProtoCacheObjectJI_V,
-      kProtoCacheObjectJ_J,
-      kProtoCacheObjectJJ_J,
-      kProtoCacheObjectJJ_V,
-      kProtoCacheObjectJ_Object,
-      kProtoCacheObjectJObject_V,
-      kProtoCacheObjectJObject_Object,
-      kProtoCacheCharArrayICharArrayII_V,
-      kProtoCacheObjectIObjectII_V,
-      kProtoCacheIICharArrayI_V,
-      kProtoCacheByteArrayIII_String,
-      kProtoCacheIICharArray_String,
-      kProtoCacheString_String,
-      kProtoCache_V,
-      kProtoCacheByteArray_V,
-      kProtoCacheByteArrayI_V,
-      kProtoCacheByteArrayII_V,
-      kProtoCacheByteArrayIII_V,
-      kProtoCacheByteArrayIIString_V,
-      kProtoCacheByteArrayString_V,
-      kProtoCacheByteArrayIICharset_V,
-      kProtoCacheByteArrayCharset_V,
-      kProtoCacheCharArray_V,
-      kProtoCacheCharArrayII_V,
-      kProtoCacheIICharArray_V,
-      kProtoCacheIntArrayII_V,
-      kProtoCacheString_V,
-      kProtoCacheStringBuffer_V,
-      kProtoCacheStringBuilder_V,
-      kProtoCacheLast
-    };
-
-  private:
-    /**
-     * The maximum number of method parameters we support in the ProtoDef.
-     */
-    static constexpr uint32_t kProtoMaxParams = 6;
-
-    /**
-     * The method signature (proto) definition using cached class indexes.
-     * The return_type and params are used with the IndexCache to look up
-     * appropriate class indexes to be passed to DexFile::FindProtoId().
-     */
-    struct ProtoDef {
-      ClassCacheIndex return_type;
-      uint8_t param_count;
-      ClassCacheIndex params[kProtoMaxParams];
-    };
-
-    /**
-     * The method definition using cached class, name and proto indexes.
-     * The class index, method name index and proto index are used with
-     * IndexCache to look up appropriate parameters for DexFile::FindMethodId().
-     */
-    struct MethodDef {
-      ClassCacheIndex declaring_class;
-      NameCacheIndex name;
-      ProtoCacheIndex proto;
-    };
-
-    /**
-     * The definition of an intrinsic function binds the method definition
-     * to an Intrinsic.
-     */
-    struct IntrinsicDef {
-      MethodDef method_def;
-      InlineMethod intrinsic;
-    };
-
-    /**
-     * Cache for class, method name and method signature indexes used during
-     * intrinsic function lookup to avoid multiple lookups of the same items.
-     *
-     * Many classes have multiple intrinsics and/or they are used in multiple
-     * method signatures and we want to avoid repeated lookups since they are
-     * not exactly cheap. The method names and method signatures are sometimes
-     * reused and therefore cached as well.
-     */
-    struct IndexCache {
-      IndexCache();
-
-      uint32_t class_indexes[kClassCacheLast - kClassCacheFirst];
-      uint32_t name_indexes[kNameCacheLast - kNameCacheFirst];
-      uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst];
-    };
-
-    static const char* const kClassCacheNames[];
-    static const char* const kNameCacheNames[];
-    static const ProtoDef kProtoCacheDefs[];
-    static const IntrinsicDef kIntrinsicMethods[];
-
-    static const uint32_t kIndexNotFound = static_cast<uint32_t>(-1);
-    static const uint32_t kIndexUnresolved = static_cast<uint32_t>(-2);
-
-    static uint32_t FindClassIndex(const DexFile* dex_file, IndexCache* cache,
-                                   ClassCacheIndex index);
-    static uint32_t FindNameIndex(const DexFile* dex_file, IndexCache* cache,
-                                  NameCacheIndex index);
-    static uint32_t FindProtoIndex(const DexFile* dex_file, IndexCache* cache,
-                                   ProtoCacheIndex index);
-    static uint32_t FindMethodIndex(const DexFile* dex_file, IndexCache* cache,
-                                    const MethodDef& method_def);
-
-    /**
-     * Find all known intrinsic methods in the dex_file and cache their indices.
-     *
-     * Only DexFileToMethodInlinerMap may call this function to initialize the inliner.
-     */
-    void FindIntrinsics(const DexFile* dex_file) REQUIRES(lock_);
-
-    friend class DexFileToMethodInlinerMap;
-
-    bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) REQUIRES(!lock_);
-
-    ReaderWriterMutex lock_;
-    /*
-     * Maps method indexes (for the particular DexFile) to Intrinsic defintions.
-     */
-    SafeMap<uint32_t, InlineMethod> inline_methods_ GUARDED_BY(lock_);
-    const DexFile* dex_file_;
-
-    DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc
deleted file mode 100644
index 2fec183..0000000
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-
-#include <algorithm>
-#include <utility>
-#include "thread.h"
-#include "thread-inl.h"
-#include "base/mutex.h"
-#include "base/mutex-inl.h"
-#include "base/logging.h"
-#include "driver/compiler_driver.h"
-
-#include "dex_file_to_method_inliner_map.h"
-
-namespace art {
-
-DexFileToMethodInlinerMap::DexFileToMethodInlinerMap()
-    : lock_("DexFileToMethodInlinerMap lock", kDexFileToMethodInlinerMapLock) {
-}
-
-DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() {
-  for (auto& entry : inliners_) {
-    delete entry.second;
-  }
-}
-
-DexFileMethodInliner* DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) {
-  Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, lock_);
-    auto it = inliners_.find(dex_file);
-    if (it != inliners_.end()) {
-      return it->second;
-    }
-  }
-
-  // We need to acquire our lock_ to modify inliners_ but we want to release it
-  // before we initialize the new inliner. However, we need to acquire the
-  // new inliner's lock_ before we release our lock_ to prevent another thread
-  // from using the uninitialized inliner. This requires explicit calls to
-  // ExclusiveLock()/ExclusiveUnlock() on one of the locks, the other one
-  // can use WriterMutexLock.
-  DexFileMethodInliner* locked_inliner;
-  {
-    WriterMutexLock mu(self, lock_);
-    DexFileMethodInliner** inliner = &inliners_[dex_file];  // inserts new entry if not found
-    if (*inliner) {
-      return *inliner;
-    }
-    *inliner = new DexFileMethodInliner;
-    DCHECK(*inliner != nullptr);
-    locked_inliner = *inliner;
-    locked_inliner->lock_.ExclusiveLock(self);  // Acquire inliner's lock_ before releasing lock_.
-  }
-  locked_inliner->FindIntrinsics(dex_file);
-  locked_inliner->lock_.ExclusiveUnlock(self);
-  return locked_inliner;
-}
-
-}  // namespace art
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h
deleted file mode 100644
index 215dc12..0000000
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_
-#define ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_
-
-#include <map>
-#include <vector>
-#include "base/macros.h"
-#include "base/mutex.h"
-
-#include "dex/quick/dex_file_method_inliner.h"
-
-namespace art {
-
-class CompilerDriver;
-class DexFile;
-
-/**
- * Map each DexFile to its DexFileMethodInliner.
- *
- * The method inliner is created and initialized the first time it's requested
- * for a particular DexFile.
- */
-class DexFileToMethodInlinerMap {
-  public:
-    DexFileToMethodInlinerMap();
-    ~DexFileToMethodInlinerMap();
-
-    DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) NO_THREAD_SAFETY_ANALYSIS;
-        // TODO: There is an irregular non-scoped use of locks that defeats annotalysis with -O0.
-        // Fix the NO_THREAD_SAFETY_ANALYSIS when this works and add the appropriate LOCKS_EXCLUDED.
-
-  private:
-    ReaderWriterMutex lock_;
-    std::map<const DexFile*, DexFileMethodInliner*> inliners_ GUARDED_BY(lock_);
-
-    DISALLOW_COPY_AND_ASSIGN(DexFileToMethodInlinerMap);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index 2532bda..932eb51 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -16,7 +16,6 @@
 
 #include "quick_compiler_callbacks.h"
 
-#include "quick/dex_file_to_method_inliner_map.h"
 #include "verifier/method_verifier-inl.h"
 #include "verification_results.h"
 
@@ -24,8 +23,6 @@
 
 void QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
   verification_results_->ProcessVerifiedMethod(verifier);
-  MethodReference ref = verifier->GetMethodReference();
-  method_inliner_map_->GetMethodInliner(ref.dex_file)->AnalyseMethodCode(verifier);
 }
 
 void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 824194c..34fd88b 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -22,19 +22,15 @@
 namespace art {
 
 class VerificationResults;
-class DexFileToMethodInlinerMap;
 
 class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
   public:
     QuickCompilerCallbacks(VerificationResults* verification_results,
-                           DexFileToMethodInlinerMap* method_inliner_map,
                            CompilerCallbacks::CallbackMode mode)
         : CompilerCallbacks(mode),
           verification_results_(verification_results),
-          method_inliner_map_(method_inliner_map),
           verifier_deps_(nullptr) {
       CHECK(verification_results != nullptr);
-      CHECK(method_inliner_map != nullptr);
     }
 
     ~QuickCompilerCallbacks() { }
@@ -59,7 +55,6 @@
 
   private:
     VerificationResults* const verification_results_;
-    DexFileToMethodInlinerMap* const method_inliner_map_;
     verifier::VerifierDeps* verifier_deps_;
 };
 
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 6863f42..5063d71 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -21,17 +21,14 @@
 #include "compiler_driver.h"
 #include "compiler_options.h"
 #include "dex/verification_results.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 
 namespace art {
 
 TEST(CompiledMethodStorage, Deduplicate) {
   CompilerOptions compiler_options;
   VerificationResults verification_results(&compiler_options);
-  DexFileToMethodInlinerMap method_inliner_map;
   CompilerDriver driver(&compiler_options,
                         &verification_results,
-                        &method_inliner_map,
                         Compiler::kOptimizing,
                         /* instruction_set_ */ kNone,
                         /* instruction_set_features */ nullptr,
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 3a260f5..2d0dd3c 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -26,7 +26,7 @@
 #include "dex_compilation_unit.h"
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "handle_scope-inl.h"
 
 namespace art {
@@ -37,7 +37,7 @@
 
 inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa,
                                                            const DexCompilationUnit* mUnit) {
-  return soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader());
+  return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Decode();
 }
 
 inline mirror::Class* CompilerDriver::ResolveClass(
@@ -45,7 +45,7 @@
     Handle<mirror::ClassLoader> class_loader, uint16_t cls_index,
     const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
       *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
   DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
@@ -60,7 +60,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
-  DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   const DexFile::MethodId& referrer_method_id =
       mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
   return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
@@ -95,7 +95,7 @@
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t field_idx, bool is_static) {
-  DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx,
                                  is_static);
 }
@@ -258,7 +258,7 @@
     ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
     uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
-  DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+  DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
   ArtMethod* resolved_method =
       check_incompatible_class_change
           ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
@@ -293,146 +293,6 @@
   }
 }
 
-inline int CompilerDriver::IsFastInvoke(
-    ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-    Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
-    mirror::Class* referrer_class, ArtMethod* resolved_method, InvokeType* invoke_type,
-    MethodReference* target_method, const MethodReference* devirt_target,
-    uintptr_t* direct_code, uintptr_t* direct_method) {
-  // Don't try to fast-path if we don't understand the caller's class.
-  // Referrer_class is the class that this invoke is contained in.
-  if (UNLIKELY(referrer_class == nullptr)) {
-    return 0;
-  }
-  StackHandleScope<2> hs(soa.Self());
-  // Methods_class is the class refered to by the class_idx field of the methodId the method_idx is
-  // pointing to.
-  // For example in
-  //   .class LABC;
-  //   .super LDEF;
-  //   .method hi()V
-  //     ...
-  //     invoke-super {p0}, LDEF;->hi()V
-  //     ...
-  //   .end method
-  // the referrer_class is 'ABC' and the methods_class is DEF. Note that the methods class is 'DEF'
-  // even if 'DEF' inherits the method from it's superclass.
-  Handle<mirror::Class> methods_class(hs.NewHandle(mUnit->GetClassLinker()->ResolveType(
-      *target_method->dex_file,
-      target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_,
-      dex_cache,
-      class_loader)));
-  DCHECK(methods_class.Get() != nullptr);
-  mirror::Class* methods_declaring_class = resolved_method->GetDeclaringClass();
-  if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_declaring_class, resolved_method,
-                                                        dex_cache.Get(),
-                                                        target_method->dex_method_index))) {
-    return 0;
-  }
-  // Sharpen a virtual call into a direct call when the target is known not to have been
-  // overridden (ie is final).
-  const bool same_dex_file = target_method->dex_file == mUnit->GetDexFile();
-  bool can_sharpen_virtual_based_on_type = same_dex_file &&
-      (*invoke_type == kVirtual) && (resolved_method->IsFinal() ||
-                                     methods_declaring_class->IsFinal());
-  // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of
-  // the super class.
-  const PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet());
-  // TODO We should be able to sharpen if we are going into the boot image as well.
-  bool can_sharpen_super_based_on_type = same_dex_file &&
-      (*invoke_type == kSuper) &&
-      !methods_class->IsInterface() &&
-      (referrer_class != methods_declaring_class) &&
-      referrer_class->IsSubClass(methods_declaring_class) &&
-      resolved_method->GetMethodIndex() < methods_declaring_class->GetVTableLength() &&
-      (methods_declaring_class->GetVTableEntry(
-          resolved_method->GetMethodIndex(), pointer_size) == resolved_method) &&
-      resolved_method->IsInvokable();
-  // TODO We should be able to sharpen if we are going into the boot image as well.
-  bool can_sharpen_interface_super_based_on_type = same_dex_file &&
-      (*invoke_type == kSuper) &&
-      methods_class->IsInterface() &&
-      methods_class->IsAssignableFrom(referrer_class) &&
-      resolved_method->IsInvokable();
-
-  if (can_sharpen_virtual_based_on_type ||
-      can_sharpen_super_based_on_type ||
-      can_sharpen_interface_super_based_on_type) {
-    // Sharpen a virtual call into a direct call. The method_idx is into referrer's
-    // dex cache, check that this resolved method is where we expect it.
-    CHECK_EQ(target_method->dex_file, mUnit->GetDexFile());
-    DCHECK_EQ(dex_cache.Get(), mUnit->GetClassLinker()->FindDexCache(
-        soa.Self(), *mUnit->GetDexFile(), false));
-    CHECK_EQ(referrer_class->GetDexCache()->GetResolvedMethod(
-        target_method->dex_method_index, pointer_size),
-             resolved_method) << PrettyMethod(resolved_method);
-    int stats_flags = kFlagMethodResolved;
-    GetCodeAndMethodForDirectCall(/*out*/invoke_type,
-                                  kDirect,  // Sharp type
-                                  false,    // The dex cache is guaranteed to be available
-                                  referrer_class, resolved_method,
-                                  /*out*/&stats_flags,
-                                  target_method,
-                                  /*out*/direct_code,
-                                  /*out*/direct_method);
-    DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method);
-    if (*invoke_type == kDirect) {
-      stats_flags |= kFlagsMethodResolvedVirtualMadeDirect;
-    }
-    return stats_flags;
-  }
-
-  if ((*invoke_type == kVirtual || *invoke_type == kInterface) && devirt_target != nullptr) {
-    // Post-verification callback recorded a more precise invoke target based on its type info.
-    ArtMethod* called_method;
-    ClassLinker* class_linker = mUnit->GetClassLinker();
-    if (LIKELY(devirt_target->dex_file == mUnit->GetDexFile())) {
-      called_method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
-          *devirt_target->dex_file, devirt_target->dex_method_index, dex_cache, class_loader,
-          nullptr, kVirtual);
-    } else {
-      auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(*devirt_target->dex_file,
-                                                                       class_loader.Get())));
-      called_method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
-          *devirt_target->dex_file, devirt_target->dex_method_index, target_dex_cache,
-          class_loader, nullptr, kVirtual);
-    }
-    CHECK(called_method != nullptr);
-    CHECK(called_method->IsInvokable());
-    int stats_flags = kFlagMethodResolved;
-    GetCodeAndMethodForDirectCall(/*out*/invoke_type,
-                                  kDirect,  // Sharp type
-                                  true,     // The dex cache may not be available
-                                  referrer_class, called_method,
-                                  /*out*/&stats_flags,
-                                  target_method,
-                                  /*out*/direct_code,
-                                  /*out*/direct_method);
-    DCHECK_NE(*invoke_type, kSuper);
-    if (*invoke_type == kDirect) {
-      stats_flags |= kFlagsMethodResolvedPreciseTypeDevirtualization;
-    }
-    return stats_flags;
-  }
-
-  if (UNLIKELY(*invoke_type == kSuper)) {
-    // Unsharpened super calls are suspicious so go slow-path.
-    return 0;
-  }
-
-  // Sharpening failed so generate a regular resolved method dispatch.
-  int stats_flags = kFlagMethodResolved;
-  GetCodeAndMethodForDirectCall(/*out*/invoke_type,
-                                *invoke_type,  // Sharp type
-                                false,         // The dex cache is guaranteed to be available
-                                referrer_class, resolved_method,
-                                /*out*/&stats_flags,
-                                target_method,
-                                /*out*/direct_code,
-                                /*out*/direct_method);
-  return stats_flags;
-}
-
 inline bool CompilerDriver::IsMethodsClassInitialized(mirror::Class* referrer_class,
                                                       ArtMethod* resolved_method) {
   if (!resolved_method->IsStatic()) {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a149c07..2ec3f16 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -44,9 +44,8 @@
 #include "dex/dex_to_dex_compiler.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
-#include "dex/quick/dex_file_method_inliner.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_options.h"
+#include "intrinsics_enum.h"
 #include "jni_internal.h"
 #include "object_lock.h"
 #include "runtime.h"
@@ -60,7 +59,7 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/throwable.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "handle_scope-inl.h"
 #include "thread.h"
@@ -353,7 +352,6 @@
 CompilerDriver::CompilerDriver(
     const CompilerOptions* compiler_options,
     VerificationResults* verification_results,
-    DexFileToMethodInlinerMap* method_inliner_map,
     Compiler::Kind compiler_kind,
     InstructionSet instruction_set,
     const InstructionSetFeatures* instruction_set_features,
@@ -370,7 +368,6 @@
     const ProfileCompilationInfo* profile_compilation_info)
     : compiler_options_(compiler_options),
       verification_results_(verification_results),
-      method_inliner_map_(method_inliner_map),
       compiler_(Compiler::Create(this, compiler_kind)),
       compiler_kind_(compiler_kind),
       instruction_set_(instruction_set == kArm ? kThumb2: instruction_set),
@@ -401,7 +398,6 @@
       dex_to_dex_references_(),
       current_dex_to_dex_methods_(nullptr) {
   DCHECK(compiler_options_ != nullptr);
-  DCHECK(method_inliner_map_ != nullptr);
 
   compiler_->Init();
 
@@ -463,6 +459,29 @@
 }
 #undef CREATE_TRAMPOLINE
 
+static void SetupIntrinsic(Thread* self,
+                           Intrinsics intrinsic,
+                           InvokeType invoke_type,
+                           const char* class_name,
+                           const char* method_name,
+                           const char* signature)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  PointerSize image_size = class_linker->GetImagePointerSize();
+  mirror::Class* cls = class_linker->FindSystemClass(self, class_name);
+  if (cls == nullptr) {
+    LOG(FATAL) << "Could not find class of intrinsic " << class_name;
+  }
+  ArtMethod* method = (invoke_type == kStatic || invoke_type == kDirect)
+      ? cls->FindDeclaredDirectMethod(method_name, signature, image_size)
+      : cls->FindDeclaredVirtualMethod(method_name, signature, image_size);
+  if (method == nullptr) {
+    LOG(FATAL) << "Could not find method of intrinsic " << class_name << method_name << signature;
+  }
+  DCHECK_EQ(method->GetInvokeType(), invoke_type);
+  method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
+}
+
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
                                 TimingLogger* timings) {
@@ -477,6 +496,17 @@
   // 3) Attempt to verify all classes
   // 4) Attempt to initialize image classes, and trivially initialized classes
   PreCompile(class_loader, dex_files, timings);
+  if (IsBootImage()) {
+    // We don't need to setup the intrinsics for non boot image compilation, as
+    // those compilations will pick up a boot image that have the ArtMethod already
+    // set with the intrinsics flag.
+    ScopedObjectAccess soa(Thread::Current());
+#define OPTIMIZING_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, ClassName, MethodName, Signature) \
+  SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+  }
   // Compile:
   // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
   //    compilation.
@@ -535,7 +565,7 @@
   ScopedObjectAccess soa(self);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
   return GetDexToDexCompilationLevel(self, driver, class_loader, dex_file, class_def);
 }
 
@@ -595,9 +625,14 @@
               : optimizer::DexToDexCompilationLevel::kRequired);
     }
   } else if ((access_flags & kAccNative) != 0) {
-    // Are we extracting only and have support for generic JNI down calls?
-    if (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
-        InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
+    const InstructionSet instruction_set = driver->GetInstructionSet();
+    const bool use_generic_jni =
+        // Are we extracting only and have support for generic JNI down calls?
+        (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
+             InstructionSetHasGenericJniStub(instruction_set)) ||
+        // Always punt to generic JNI for MIPS because of no support for @CriticalNative. b/31743474
+        (instruction_set == kMips || instruction_set == kMips64);
+    if (use_generic_jni) {
       // Leaving this empty will trigger the generic JNI version
     } else {
       // Look-up the ArtMethod associated with this code_item (if any)
@@ -605,7 +640,7 @@
       ScopedObjectAccess soa(self);
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle(
-          soa.Decode<mirror::ClassLoader*>(class_loader)));
+          soa.Decode<mirror::ClassLoader>(class_loader)));
 
       // TODO: Lookup annotation from DexFile directly without resolving method.
       ArtMethod* method =
@@ -1621,7 +1656,7 @@
   {
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader_handle(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader())));
     resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false);
     referrer_class = resolved_field != nullptr
         ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
@@ -1654,12 +1689,8 @@
   }
 }
 
-void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
-                                                   bool no_guarantee_of_dex_cache_entry,
-                                                   const mirror::Class* referrer_class,
+void CompilerDriver::GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
                                                    ArtMethod* method,
-                                                   int* stats_flags,
-                                                   MethodReference* target_method,
                                                    uintptr_t* direct_code,
                                                    uintptr_t* direct_method) {
   // For direct and static methods compute possible direct_code and direct_method values, ie
@@ -1671,15 +1702,11 @@
   Runtime* const runtime = Runtime::Current();
   gc::Heap* const heap = runtime->GetHeap();
   auto* cl = runtime->GetClassLinker();
-  const auto pointer_size = cl->GetImagePointerSize();
   bool use_dex_cache = GetCompilerOptions().GetCompilePic();  // Off by default
   const bool compiling_boot = heap->IsCompilingBoot();
   // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
   const bool force_relocations = (compiling_boot ||
                                   GetCompilerOptions().GetIncludePatchInformation());
-  if (sharp_type != kStatic && sharp_type != kDirect) {
-    return;
-  }
   // TODO: support patching on all architectures.
   use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
   mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -1687,14 +1714,12 @@
   if (!use_dex_cache) {
     if (!method_code_in_boot) {
       use_dex_cache = true;
-    } else {
-      bool has_clinit_trampoline =
-          method->IsStatic() && !declaring_class->IsInitialized();
-      if (has_clinit_trampoline && declaring_class != referrer_class) {
-        // Ensure we run the clinit trampoline unless we are invoking a static method in the same
-        // class.
-        use_dex_cache = true;
-      }
+    } else if (method->IsStatic() &&
+               declaring_class != referrer_class &&
+               !declaring_class->IsInitialized()) {
+      // Ensure we run the clinit trampoline unless we are invoking a static method in the same
+      // class.
+      use_dex_cache = true;
     }
   }
   if (runtime->UseJitCompilation()) {
@@ -1705,9 +1730,7 @@
       use_dex_cache = true;
     }
   }
-  if (method_code_in_boot) {
-    *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
-  }
+
   if (!use_dex_cache && force_relocations) {
     bool is_in_image;
     if (IsBootImage()) {
@@ -1724,39 +1747,8 @@
       use_dex_cache = true;
     }
   }
-  // The method is defined not within this dex file. We need a dex cache slot within the current
-  // dex file or direct pointers.
-  bool must_use_direct_pointers = false;
-  mirror::DexCache* dex_cache = declaring_class->GetDexCache();
-  if (target_method->dex_file == dex_cache->GetDexFile() &&
-    !(runtime->UseJitCompilation() && dex_cache->GetResolvedMethod(
-        method->GetDexMethodIndex(), pointer_size) == nullptr)) {
-    target_method->dex_method_index = method->GetDexMethodIndex();
-  } else {
-    if (no_guarantee_of_dex_cache_entry) {
-      // See if the method is also declared in this dex cache.
-      uint32_t dex_method_idx = method->FindDexMethodIndexInOtherDexFile(
-          *target_method->dex_file, target_method->dex_method_index);
-      if (dex_method_idx != DexFile::kDexNoIndex) {
-        target_method->dex_method_index = dex_method_idx;
-      } else {
-        if (force_relocations && !use_dex_cache) {
-          target_method->dex_method_index = method->GetDexMethodIndex();
-          target_method->dex_file = dex_cache->GetDexFile();
-        }
-        must_use_direct_pointers = true;
-      }
-    }
-  }
-  if (use_dex_cache) {
-    if (must_use_direct_pointers) {
-      // Fail. Test above showed the only safe dispatch was via the dex cache, however, the direct
-      // pointers are required as the dex cache lacks an appropriate entry.
-      VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
-    } else {
-      *type = sharp_type;
-    }
-  } else {
+
+  if (!use_dex_cache) {
     bool method_in_image = false;
     const std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
     for (gc::space::ImageSpace* image_space : image_spaces) {
@@ -1772,87 +1764,15 @@
       // the method and its code are / will be. We don't sharpen to interpreter bridge since we
       // check IsQuickToInterpreterBridge above.
       CHECK(!method->IsAbstract());
-      *type = sharp_type;
       *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
       *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
-      target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
-      target_method->dex_method_index = method->GetDexMethodIndex();
-    } else if (!must_use_direct_pointers) {
+    } else {
       // Set the code and rely on the dex cache for the method.
-      *type = sharp_type;
-      if (force_relocations) {
-        *direct_code = -1;
-        target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
-        target_method->dex_method_index = method->GetDexMethodIndex();
-      } else {
-        *direct_code = compiler_->GetEntryPointOf(method);
-      }
-    } else {
-      // Direct pointers were required but none were available.
-      VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
+      *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
     }
   }
 }
 
-bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
-                                       bool update_stats, bool enable_devirtualization,
-                                       InvokeType* invoke_type, MethodReference* target_method,
-                                       int* vtable_idx, uintptr_t* direct_code,
-                                       uintptr_t* direct_method) {
-  InvokeType orig_invoke_type = *invoke_type;
-  int stats_flags = 0;
-  ScopedObjectAccess soa(Thread::Current());
-  // Try to resolve the method and compiling method's class.
-  StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())));
-  uint32_t method_idx = target_method->dex_method_index;
-  ArtMethod* resolved_method = ResolveMethod(
-      soa, dex_cache, class_loader, mUnit, method_idx, orig_invoke_type);
-  auto h_referrer_class = hs.NewHandle(resolved_method != nullptr ?
-      ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit) : nullptr);
-  bool result = false;
-  if (resolved_method != nullptr) {
-    *vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type);
-
-    if (enable_devirtualization && mUnit->GetVerifiedMethod() != nullptr) {
-      const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc);
-
-      stats_flags = IsFastInvoke(
-          soa, dex_cache, class_loader, mUnit, h_referrer_class.Get(), resolved_method,
-          invoke_type, target_method, devirt_target, direct_code, direct_method);
-      result = stats_flags != 0;
-    } else {
-      // Devirtualization not enabled. Inline IsFastInvoke(), dropping the devirtualization parts.
-      if (UNLIKELY(h_referrer_class.Get() == nullptr) ||
-          UNLIKELY(!h_referrer_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
-                                                            resolved_method, dex_cache.Get(),
-                                                            target_method->dex_method_index)) ||
-          *invoke_type == kSuper) {
-        // Slow path. (Without devirtualization, all super calls go slow path as well.)
-      } else {
-        // Sharpening failed so generate a regular resolved method dispatch.
-        stats_flags = kFlagMethodResolved;
-        GetCodeAndMethodForDirectCall(
-            invoke_type, *invoke_type, false, h_referrer_class.Get(), resolved_method, &stats_flags,
-            target_method, direct_code, direct_method);
-        result = true;
-      }
-    }
-  }
-  if (!result) {
-    // Conservative defaults.
-    *vtable_idx = -1;
-    *direct_code = 0u;
-    *direct_method = 0u;
-  }
-  if (update_stats) {
-    ProcessedInvoke(orig_invoke_type, stats_flags);
-  }
-  return result;
-}
-
 const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
                                                         uint32_t method_idx) const {
   MethodReference ref(dex_file, method_idx);
@@ -2080,7 +2000,7 @@
     ScopedObjectAccess soa(self);
     StackHandleScope<2> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
         soa.Self(), dex_file, false)));
     // Resolve the class.
@@ -2177,7 +2097,7 @@
     const DexFile& dex_file = *manager_->GetDexFile();
     StackHandleScope<2> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(manager_->GetClassLoader())));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(manager_->GetClassLoader())));
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
         dex_file,
         class_loader.Get())));
@@ -2276,7 +2196,7 @@
     jobject jclass_loader = manager_->GetClassLoader();
     StackHandleScope<3> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     if (klass.Get() == nullptr) {
@@ -2364,7 +2284,7 @@
     jobject jclass_loader = manager_->GetClassLoader();
     StackHandleScope<3> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     // Class might have failed resolution. Then don't set it to verified.
@@ -2426,7 +2346,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<3> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     Handle<mirror::Class> klass(
         hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader)));
 
@@ -2661,7 +2581,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<3> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
     Handle<mirror::DexCache> dex_cache;
@@ -2904,18 +2824,6 @@
   return oss.str();
 }
 
-bool CompilerDriver::IsStringTypeIndex(uint16_t type_index, const DexFile* dex_file) {
-  const char* type = dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_index));
-  return strcmp(type, "Ljava/lang/String;") == 0;
-}
-
-bool CompilerDriver::IsStringInit(uint32_t method_index, const DexFile* dex_file, int32_t* offset) {
-  DexFileMethodInliner* inliner = GetMethodInlinerMap()->GetMethodInliner(dex_file);
-  const PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet());
-  *offset = inliner->GetOffsetForStringInit(method_index, pointer_size);
-  return inliner->IsStringInitMethodIndex(method_index);
-}
-
 bool CompilerDriver::MayInlineInternal(const DexFile* inlined_from,
                                        const DexFile* inlined_into) const {
   // We're not allowed to inline across dex files if we're the no-inline-from dex file.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index ee21efa..52a04cc 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -57,7 +57,6 @@
 class CompiledMethod;
 class CompilerOptions;
 class DexCompilationUnit;
-class DexFileToMethodInlinerMap;
 struct InlineIGetIPutData;
 class InstructionSetFeatures;
 class ParallelCompilationManager;
@@ -88,7 +87,6 @@
   // classes.
   CompilerDriver(const CompilerOptions* compiler_options,
                  VerificationResults* verification_results,
-                 DexFileToMethodInlinerMap* method_inliner_map,
                  Compiler::Kind compiler_kind,
                  InstructionSet instruction_set,
                  const InstructionSetFeatures* instruction_set_features,
@@ -133,10 +131,6 @@
     return verification_results_;
   }
 
-  DexFileToMethodInlinerMap* GetMethodInlinerMap() const {
-    return method_inliner_map_;
-  }
-
   InstructionSet GetInstructionSet() const {
     return instruction_set_;
   }
@@ -328,16 +322,6 @@
       ArtMethod* resolved_method, InvokeType type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Can we fast-path an INVOKE? If no, returns 0. If yes, returns a non-zero opaque flags value
-  // for ProcessedInvoke() and computes the necessary lowering info.
-  int IsFastInvoke(
-      ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-      Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
-      mirror::Class* referrer_class, ArtMethod* resolved_method, InvokeType* invoke_type,
-      MethodReference* target_method, const MethodReference* devirt_target,
-      uintptr_t* direct_code, uintptr_t* direct_method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Is method's class initialized for an invoke?
   // For static invokes to determine whether we need to consider potential call to <clinit>().
   // For non-static invokes, assuming a non-null reference, the class is always initialized.
@@ -371,14 +355,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
 
-  // Can we fastpath a interface, super class or virtual method call? Computes method's vtable
-  // index.
-  bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
-                         bool update_stats, bool enable_devirtualization,
-                         InvokeType* type, MethodReference* target_method, int* vtable_idx,
-                         uintptr_t* direct_code, uintptr_t* direct_method)
-      REQUIRES(!Locks::mutator_lock_);
-
   const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
   bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
 
@@ -450,9 +426,6 @@
   // Get memory usage during compilation.
   std::string GetMemoryUsageString(bool extended) const;
 
-  bool IsStringTypeIndex(uint16_t type_index, const DexFile* dex_file);
-  bool IsStringInit(uint32_t method_index, const DexFile* dex_file, int32_t* offset);
-
   void SetHadHardVerifierFailure() {
     had_hard_verifier_failure_ = true;
   }
@@ -538,14 +511,10 @@
 
  public:  // TODO make private or eliminate.
   // Compute constant code and method pointers when possible.
-  void GetCodeAndMethodForDirectCall(/*out*/InvokeType* type,
-                                     InvokeType sharp_type,
-                                     bool no_guarantee_of_dex_cache_entry,
-                                     const mirror::Class* referrer_class,
+  void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
                                      ArtMethod* method,
-                                     /*out*/int* stats_flags,
-                                     MethodReference* target_method,
-                                     uintptr_t* direct_code, uintptr_t* direct_method)
+                                     /* out */ uintptr_t* direct_code,
+                                     /* out */ uintptr_t* direct_method)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
@@ -628,7 +597,6 @@
 
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
-  DexFileToMethodInlinerMap* const method_inliner_map_;
 
   std::unique_ptr<Compiler> compiler_;
   Compiler::Kind compiler_kind_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index b9a5a78..96f17ac 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -32,7 +32,7 @@
 #include "mirror/object-inl.h"
 #include "handle_scope-inl.h"
 #include "jit/offline_profiling_info.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -83,7 +83,7 @@
       ScopedObjectAccess soa(Thread::Current());
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> loader(
-          hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+          hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
       mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader);
       CHECK(c != nullptr);
       const auto pointer_size = class_linker->GetImagePointerSize();
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index ca0869a..0c06090 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -26,7 +26,7 @@
 #include "invoke_type.h"
 #include "mirror/object-inl.h"
 #include "oat.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 86f91c5..f9e5cb9 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -31,7 +31,7 @@
 #include "oat_quick_method_header.h"
 #include "optimizing/stack_map_stream.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "handle_scope-inl.h"
 #include "thread.h"
 
@@ -45,7 +45,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<2> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle"))));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("ExceptionHandle"))));
     my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
     ASSERT_TRUE(my_klass_ != nullptr);
     Handle<mirror::Class> klass(hs.NewHandle(my_klass_));
@@ -219,7 +219,7 @@
   ASSERT_TRUE(internal != nullptr);
   jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
   ASSERT_TRUE(ste_array != nullptr);
-  auto* trace_array = soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array);
+  auto trace_array = soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(ste_array);
 
   ASSERT_TRUE(trace_array != nullptr);
   ASSERT_TRUE(trace_array->Get(0) != nullptr);
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index a18935f..4689c9d 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -33,167 +33,315 @@
 #include "lock_word.h"
 #include "mirror/object-inl.h"
 #include "oat_writer.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "signal_catcher.h"
 #include "utils.h"
 
 namespace art {
 
+static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS;
+
+struct CompilationHelper {
+  std::vector<std::string> dex_file_locations;
+  std::vector<ScratchFile> image_locations;
+  std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
+  std::vector<ScratchFile> image_files;
+  std::vector<ScratchFile> oat_files;
+  std::vector<ScratchFile> vdex_files;
+  std::string image_dir;
+
+  void Compile(CompilerDriver* driver,
+               ImageHeader::StorageMode storage_mode);
+
+  std::vector<size_t> GetImageObjectSectionSizes();
+
+  ~CompilationHelper();
+};
+
 class ImageTest : public CommonCompilerTest {
  protected:
   virtual void SetUp() {
     ReserveImageSpace();
     CommonCompilerTest::SetUp();
   }
+
   void TestWriteRead(ImageHeader::StorageMode storage_mode);
+
+  void Compile(ImageHeader::StorageMode storage_mode,
+               CompilationHelper& out_helper,
+               const std::string& extra_dex = "",
+               const std::string& image_class = "");
+
+  std::unordered_set<std::string>* GetImageClasses() OVERRIDE {
+    return new std::unordered_set<std::string>(image_classes_);
+  }
+
+ private:
+  std::unordered_set<std::string> image_classes_;
 };
 
-void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
-  CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+CompilationHelper::~CompilationHelper() {
+  for (ScratchFile& image_file : image_files) {
+    image_file.Unlink();
+  }
+  for (ScratchFile& oat_file : oat_files) {
+    oat_file.Unlink();
+  }
+  for (ScratchFile& vdex_file : vdex_files) {
+    vdex_file.Unlink();
+  }
+  const int rmdir_result = rmdir(image_dir.c_str());
+  CHECK_EQ(0, rmdir_result);
+}
 
-  // Set inline filter values.
-  compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
-  compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() {
+  std::vector<size_t> ret;
+  for (ScratchFile& image_file : image_files) {
+    std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
+    CHECK(file.get() != nullptr);
+    ImageHeader image_header;
+    CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
+    CHECK(image_header.IsValid());
+    ret.push_back(image_header.GetImageSize());
+  }
+  return ret;
+}
 
+void CompilationHelper::Compile(CompilerDriver* driver,
+                                ImageHeader::StorageMode storage_mode) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
+
+  for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) {
+    {
+      ScopedObjectAccess soa(Thread::Current());
+      // Inject in boot class path so that the compiler driver can see it.
+      class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get());
+    }
+    class_path.push_back(dex_file.get());
+  }
+
   // Enable write for dex2dex.
-  for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
-    dex_file->EnableWrite();
+  for (const DexFile* dex_file : class_path) {
+    dex_file_locations.push_back(dex_file->GetLocation());
+    if (dex_file->IsReadOnly()) {
+      dex_file->EnableWrite();
+    }
   }
-  // Create a generic location tmp file, to be the base of the .art and .oat temporary files.
-  ScratchFile location;
-  ScratchFile image_location(location, ".art");
+  {
+    // Create a generic tmp file, to be the base of the .art and .oat temporary files.
+    ScratchFile location;
+    for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
+      std::string cur_location(StringPrintf("%s-%d.art", location.GetFilename().c_str(), i));
+      image_locations.push_back(ScratchFile(cur_location));
+    }
+  }
+  std::vector<std::string> image_filenames;
+  for (ScratchFile& file : image_locations) {
+    std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA));
+    image_filenames.push_back(image_filename);
+    size_t pos = image_filename.rfind('/');
+    CHECK_NE(pos, std::string::npos) << image_filename;
+    if (image_dir.empty()) {
+      image_dir = image_filename.substr(0, pos);
+      int mkdir_result = mkdir(image_dir.c_str(), 0700);
+      CHECK_EQ(0, mkdir_result) << image_dir;
+    }
+    image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str())));
+  }
 
-  std::string image_filename(GetSystemImageFilename(image_location.GetFilename().c_str(),
-                                                    kRuntimeISA));
-  size_t pos = image_filename.rfind('/');
-  CHECK_NE(pos, std::string::npos) << image_filename;
-  std::string image_dir(image_filename, 0, pos);
-  int mkdir_result = mkdir(image_dir.c_str(), 0700);
-  CHECK_EQ(0, mkdir_result) << image_dir;
-  ScratchFile image_file(OS::CreateEmptyFile(image_filename.c_str()));
+  std::vector<std::string> oat_filenames;
+  std::vector<std::string> vdex_filenames;
+  for (const std::string& image_filename : image_filenames) {
+    std::string oat_filename = ReplaceFileExtension(image_filename, "oat");
+    oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str())));
+    oat_filenames.push_back(oat_filename);
+    std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex");
+    vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str())));
+    vdex_filenames.push_back(vdex_filename);
+  }
 
-  std::string oat_filename = ReplaceFileExtension(image_filename, "oat");
-  ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
-
-  std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex");
-  ScratchFile vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
-
-  const uintptr_t requested_image_base = ART_BASE_ADDRESS;
   std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map;
-  std::vector<const char*> oat_filename_vector(1, oat_filename.c_str());
-  for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
-    dex_file_to_oat_index_map.emplace(dex_file, 0);
+  std::vector<const char*> oat_filename_vector;
+  for (const std::string& file : oat_filenames) {
+    oat_filename_vector.push_back(file.c_str());
   }
-  std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_,
-                                                      requested_image_base,
+  std::vector<const char*> image_filename_vector;
+  for (const std::string& file : image_filenames) {
+    image_filename_vector.push_back(file.c_str());
+  }
+  size_t image_idx = 0;
+  for (const DexFile* dex_file : class_path) {
+    dex_file_to_oat_index_map.emplace(dex_file, image_idx);
+    ++image_idx;
+  }
+  // TODO: compile_pic should be a test argument.
+  std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver,
+                                                      kRequestedImageBase,
                                                       /*compile_pic*/false,
                                                       /*compile_app_image*/false,
                                                       storage_mode,
                                                       oat_filename_vector,
                                                       dex_file_to_oat_index_map));
-  // TODO: compile_pic should be a test argument.
   {
     {
       jobject class_loader = nullptr;
       TimingLogger timings("ImageTest::WriteRead", false, false);
       TimingLogger::ScopedTiming t("CompileAll", &timings);
-      compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
-      compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
+      driver->SetDexFilesForOatFile(class_path);
+      driver->CompileAll(class_loader, class_path, &timings);
 
       t.NewTiming("WriteElf");
       SafeMap<std::string, std::string> key_value_store;
-      const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath();
-      std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
-          compiler_driver_->GetInstructionSet(),
-          compiler_driver_->GetInstructionSetFeatures(),
-          &compiler_driver_->GetCompilerOptions(),
-          oat_file.GetFile());
-      elf_writer->Start();
-      OatWriter oat_writer(/*compiling_boot_image*/true, &timings);
-      OutputStream* oat_rodata = elf_writer->StartRoData();
-      for (const DexFile* dex_file : dex_files) {
+      std::vector<const char*> dex_filename_vector;
+      for (size_t i = 0; i < class_path.size(); ++i) {
+        dex_filename_vector.push_back("");
+      }
+      key_value_store.Put(OatHeader::kBootClassPathKey,
+                          gc::space::ImageSpace::GetMultiImageBootClassPath(
+                              dex_filename_vector,
+                              oat_filename_vector,
+                              image_filename_vector));
+
+      std::vector<std::unique_ptr<ElfWriter>> elf_writers;
+      std::vector<std::unique_ptr<OatWriter>> oat_writers;
+      for (ScratchFile& oat_file : oat_files) {
+        elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(),
+                                                      driver->GetInstructionSetFeatures(),
+                                                      &driver->GetCompilerOptions(),
+                                                      oat_file.GetFile()));
+        elf_writers.back()->Start();
+        oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, &timings));
+      }
+
+      std::vector<OutputStream*> rodata;
+      std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
+      std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+      // Now that we have finalized key_value_store_, start writing the oat file.
+      for (size_t i = 0, size = oat_writers.size(); i != size; ++i) {
+        const DexFile* dex_file = class_path[i];
+        rodata.push_back(elf_writers[i]->StartRoData());
         ArrayRef<const uint8_t> raw_dex_file(
             reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
             dex_file->GetHeader().file_size_);
-        oat_writer.AddRawDexFileSource(raw_dex_file,
-                                       dex_file->GetLocation().c_str(),
-                                       dex_file->GetLocationChecksum());
-      }
-      std::unique_ptr<MemMap> opened_dex_files_map;
-      std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-      {
-        bool dex_files_ok = oat_writer.WriteAndOpenDexFiles(
-            kIsVdexEnabled ? vdex_file.GetFile() : oat_file.GetFile(),
-            oat_rodata,
-            compiler_driver_->GetInstructionSet(),
-            compiler_driver_->GetInstructionSetFeatures(),
+        oat_writers[i]->AddRawDexFileSource(raw_dex_file,
+                                            dex_file->GetLocation().c_str(),
+                                            dex_file->GetLocationChecksum());
+
+        std::unique_ptr<MemMap> cur_opened_dex_files_map;
+        std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files;
+        bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles(
+            kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(),
+            rodata.back(),
+            driver->GetInstructionSet(),
+            driver->GetInstructionSetFeatures(),
             &key_value_store,
             /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
-            &opened_dex_files_map,
-            &opened_dex_files);
+            &cur_opened_dex_files_map,
+            &cur_opened_dex_files);
         ASSERT_TRUE(dex_files_ok);
-      }
 
+        if (cur_opened_dex_files_map != nullptr) {
+          opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map));
+          for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) {
+            // dex_file_oat_index_map_.emplace(dex_file.get(), i);
+            opened_dex_files.push_back(std::move(cur_dex_file));
+          }
+        } else {
+          ASSERT_TRUE(cur_opened_dex_files.empty());
+        }
+      }
       bool image_space_ok = writer->PrepareImageAddressSpace();
       ASSERT_TRUE(image_space_ok);
 
-      linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(),
-                                              instruction_set_features_.get());
-      oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher);
-      size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
-      size_t text_size = oat_writer.GetOatSize() - rodata_size;
-      elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize());
+      for (size_t i = 0, size = oat_files.size(); i != size; ++i) {
+        linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(),
+                                                driver->GetInstructionSetFeatures());
+        OatWriter* const oat_writer = oat_writers[i].get();
+        ElfWriter* const elf_writer = elf_writers[i].get();
+        std::vector<const DexFile*> cur_dex_files(1u, class_path[i]);
+        oat_writer->PrepareLayout(driver, writer.get(), cur_dex_files, &patcher);
+        size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
+        size_t text_size = oat_writer->GetOatSize() - rodata_size;
+        elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize());
 
-      writer->UpdateOatFileLayout(/* oat_index */ 0u,
-                                  elf_writer->GetLoadedSize(),
-                                  oat_writer.GetOatDataOffset(),
-                                  oat_writer.GetOatSize());
+        writer->UpdateOatFileLayout(i,
+                                    elf_writer->GetLoadedSize(),
+                                    oat_writer->GetOatDataOffset(),
+                                    oat_writer->GetOatSize());
 
-      bool rodata_ok = oat_writer.WriteRodata(oat_rodata);
-      ASSERT_TRUE(rodata_ok);
-      elf_writer->EndRoData(oat_rodata);
+        bool rodata_ok = oat_writer->WriteRodata(rodata[i]);
+        ASSERT_TRUE(rodata_ok);
+        elf_writer->EndRoData(rodata[i]);
 
-      OutputStream* text = elf_writer->StartText();
-      bool text_ok = oat_writer.WriteCode(text);
-      ASSERT_TRUE(text_ok);
-      elf_writer->EndText(text);
+        OutputStream* text = elf_writer->StartText();
+        bool text_ok = oat_writer->WriteCode(text);
+        ASSERT_TRUE(text_ok);
+        elf_writer->EndText(text);
 
-      bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
-      ASSERT_TRUE(header_ok);
+        bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
+        ASSERT_TRUE(header_ok);
 
-      writer->UpdateOatFileHeader(/* oat_index */ 0u, oat_writer.GetOatHeader());
+        writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader());
 
-      elf_writer->WriteDynamicSection();
-      elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
-      elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
+        elf_writer->WriteDynamicSection();
+        elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
+        elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
 
-      bool success = elf_writer->End();
-      ASSERT_TRUE(success);
+        bool success = elf_writer->End();
+        ASSERT_TRUE(success);
+      }
+    }
+
+    bool success_image = writer->Write(kInvalidFd,
+                                       image_filename_vector,
+                                       oat_filename_vector);
+    ASSERT_TRUE(success_image);
+
+    for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) {
+      const char* oat_filename = oat_filenames[i].c_str();
+      std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
+      ASSERT_TRUE(oat_file != nullptr);
+      bool success_fixup = ElfWriter::Fixup(oat_file.get(),
+                                            writer->GetOatDataBegin(i));
+      ASSERT_TRUE(success_fixup);
+      ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
+                                                  << oat_filename;
     }
   }
-  // Workound bug that mcld::Linker::emit closes oat_file by reopening as dup_oat.
-  std::unique_ptr<File> dup_oat(OS::OpenFileReadWrite(oat_file.GetFilename().c_str()));
-  ASSERT_TRUE(dup_oat.get() != nullptr);
+}
 
-  {
-    std::vector<const char*> dup_oat_filename(1, dup_oat->GetPath().c_str());
-    std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str());
-    bool success_image = writer->Write(kInvalidFd,
-                                       dup_image_filename,
-                                       dup_oat_filename);
-    ASSERT_TRUE(success_image);
-    bool success_fixup = ElfWriter::Fixup(dup_oat.get(),
-                                          writer->GetOatDataBegin(0));
-    ASSERT_TRUE(success_fixup);
-
-    ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
-                                               << oat_file.GetFilename();
+void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
+                        CompilationHelper& helper,
+                        const std::string& extra_dex,
+                        const std::string& image_class) {
+  if (!image_class.empty()) {
+    image_classes_.insert(image_class);
   }
+  CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+  // Set inline filter values.
+  compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
+  compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+  image_classes_.clear();
+  if (!extra_dex.empty()) {
+    helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
+  }
+  helper.Compile(compiler_driver_.get(), storage_mode);
+  if (!image_class.empty()) {
+    // Make sure the class got initialized.
+    ScopedObjectAccess soa(Thread::Current());
+    ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+    mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
+    EXPECT_TRUE(klass != nullptr);
+    EXPECT_TRUE(klass->IsInitialized());
+  }
+}
 
-  uint64_t image_file_size;
-  size_t image_size;
-  {
+void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
+  CompilationHelper helper;
+  Compile(storage_mode, /*out*/ helper);
+  std::vector<uint64_t> image_file_sizes;
+  for (ScratchFile& image_file : helper.image_files) {
     std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
     ASSERT_TRUE(file.get() != nullptr);
     ImageHeader image_header;
@@ -209,9 +357,7 @@
     ASSERT_FALSE(space->IsImageSpace());
     ASSERT_TRUE(space != nullptr);
     ASSERT_TRUE(space->IsMallocSpace());
-
-    image_file_size = file->GetLength();
-    image_size = image_header.GetImageSize();
+    image_file_sizes.push_back(file->GetLength());
   }
 
   ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr);
@@ -225,17 +371,16 @@
   // Remove the reservation of the memory for use to load the image.
   // Need to do this before we reset the runtime.
   UnreserveImageSpace();
-  writer.reset(nullptr);
 
+  helper.extra_dex_files.clear();
   runtime_.reset();
   java_lang_dex_file_ = nullptr;
 
   MemMap::Init();
-  std::unique_ptr<const DexFile> dex(LoadExpectSingleDexFile(GetLibCoreDexFileNames()[0].c_str()));
 
   RuntimeOptions options;
   std::string image("-Ximage:");
-  image.append(image_location.GetFilename());
+  image.append(helper.image_locations[0].GetFilename());
   options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr)));
   // By default the compiler this creates will not include patch information.
   options.push_back(std::make_pair("-Xnorelocate", nullptr));
@@ -257,41 +402,45 @@
   ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace());
 
   // We loaded the runtime with an explicit image, so it must exist.
-  gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[0];
-  ASSERT_TRUE(image_space != nullptr);
-  if (storage_mode == ImageHeader::kStorageModeUncompressed) {
-    // Uncompressed, image should be smaller than file.
-    ASSERT_LE(image_size, image_file_size);
-  } else {
-    // Compressed, file should be smaller than image.
-    ASSERT_LE(image_file_size, image_size);
-  }
-
-  image_space->VerifyImageAllocations();
-  uint8_t* image_begin = image_space->Begin();
-  uint8_t* image_end = image_space->End();
-  CHECK_EQ(requested_image_base, reinterpret_cast<uintptr_t>(image_begin));
-  for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
-    const DexFile::ClassDef& class_def = dex->GetClassDef(i);
-    const char* descriptor = dex->GetClassDescriptor(class_def);
-    mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
-    EXPECT_TRUE(klass != nullptr) << descriptor;
-    if (image_classes.find(descriptor) != image_classes.end()) {
-      // Image classes should be located inside the image.
-      EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor;
-      EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor;
-    } else {
-      EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end ||
-                  reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor;
+  ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size());
+  for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) {
+    std::unique_ptr<const DexFile> dex(
+        LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str()));
+    ASSERT_TRUE(dex != nullptr);
+    uint64_t image_file_size = image_file_sizes[i];
+    gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i];
+    ASSERT_TRUE(image_space != nullptr);
+    if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+      // Uncompressed, image should be smaller than file.
+      ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size);
+    } else if (image_file_size > 16 * KB) {
+      // Compressed, file should be smaller than image. Not really valid for small images.
+      ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize());
     }
-    EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
-  }
 
-  image_file.Unlink();
-  oat_file.Unlink();
-  vdex_file.Unlink();
-  int rmdir_result = rmdir(image_dir.c_str());
-  CHECK_EQ(0, rmdir_result);
+    image_space->VerifyImageAllocations();
+    uint8_t* image_begin = image_space->Begin();
+    uint8_t* image_end = image_space->End();
+    if (i == 0) {
+      // This check is only valid for image 0.
+      CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin));
+    }
+    for (size_t j = 0; j < dex->NumClassDefs(); ++j) {
+      const DexFile::ClassDef& class_def = dex->GetClassDef(j);
+      const char* descriptor = dex->GetClassDescriptor(class_def);
+      mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
+      EXPECT_TRUE(klass != nullptr) << descriptor;
+      if (image_classes.find(descriptor) == image_classes.end()) {
+        EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end ||
+                    reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor;
+      } else {
+        // Image classes should be located inside the image.
+        EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor;
+        EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor;
+      }
+      EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false)));
+    }
+  }
 }
 
 TEST_F(ImageTest, WriteReadUncompressed) {
@@ -306,6 +455,34 @@
   TestWriteRead(ImageHeader::kStorageModeLZ4HC);
 }
 
+TEST_F(ImageTest, TestImageLayout) {
+  std::vector<size_t> image_sizes;
+  std::vector<size_t> image_sizes_extra;
+  // Compile multi-image with ImageLayoutA being the last image.
+  {
+    CompilationHelper helper;
+    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", "LMyClass;");
+    image_sizes = helper.GetImageObjectSectionSizes();
+  }
+  TearDown();
+  runtime_.reset();
+  SetUp();
+  // Compile multi-image with ImageLayoutB being the last image.
+  {
+    CompilationHelper helper;
+    Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", "LMyClass;");
+    image_sizes_extra = helper.GetImageObjectSectionSizes();
+  }
+  // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the
+  // first two images.
+  ASSERT_EQ(image_sizes.size(), image_sizes.size());
+  // Sizes of the images should be the same. These sizes are for the whole image unrounded.
+  for (size_t i = 0; i < image_sizes.size() - 1; ++i) {
+    EXPECT_EQ(image_sizes[i], image_sizes_extra[i]);
+  }
+  // Last image should be larger since it has a hash map and a string.
+  EXPECT_LT(image_sizes.back(), image_sizes_extra.back());
+}
 
 TEST_F(ImageTest, ImageHeaderIsValid) {
     uint32_t image_begin = ART_BASE_ADDRESS;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 6d86f7d..41bda60 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -45,15 +45,16 @@
 #include "gc/space/space-inl.h"
 #include "globals.h"
 #include "image.h"
+#include "imt_conflict_table.h"
 #include "intern_table.h"
 #include "linear_alloc.h"
 #include "lock_word.h"
-#include "mirror/abstract_method.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache.h"
 #include "mirror/dex_cache-inl.h"
+#include "mirror/executable.h"
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
@@ -62,7 +63,7 @@
 #include "oat_file.h"
 #include "oat_file_manager.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "handle_scope-inl.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 
@@ -389,7 +390,6 @@
   DCHECK(!IsImageBinSlotAssigned(object));
 
   // Before we stomp over the lock word, save the hash code for later.
-  Monitor::Deflate(Thread::Current(), object);;
   LockWord lw(object->GetLockWord(false));
   switch (lw.GetState()) {
     case LockWord::kFatLocked: {
@@ -490,7 +490,7 @@
   pointer_arrays_.emplace(arr, kBinArtMethodClean);
 }
 
-void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
+void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) {
   DCHECK(object != nullptr);
   size_t object_size = object->SizeOf();
 
@@ -593,7 +593,10 @@
     // else bin = kBinRegular
   }
 
-  size_t oat_index = GetOatIndex(object);
+  // Assign the oat index too.
+  DCHECK(oat_index_map_.find(object) == oat_index_map_.end());
+  oat_index_map_.emplace(object, oat_index);
+
   ImageInfo& image_info = GetImageInfo(oat_index);
 
   size_t offset_delta = RoundUp(object_size, kObjectAlignment);  // 64-bit alignment
@@ -974,39 +977,6 @@
   return nullptr;
 }
 
-void ImageWriter::CalculateObjectBinSlots(Object* obj) {
-  DCHECK(obj != nullptr);
-  // if it is a string, we want to intern it if its not interned.
-  if (obj->GetClass()->IsStringClass()) {
-    size_t oat_index = GetOatIndex(obj);
-    ImageInfo& image_info = GetImageInfo(oat_index);
-
-    // we must be an interned string that was forward referenced and already assigned
-    if (IsImageBinSlotAssigned(obj)) {
-      DCHECK_EQ(obj, FindInternedString(obj->AsString()));
-      return;
-    }
-    // Need to check if the string is already interned in another image info so that we don't have
-    // the intern tables of two different images contain the same string.
-    mirror::String* interned = FindInternedString(obj->AsString());
-    if (interned == nullptr) {
-      // Not in another image space, insert to our table.
-      interned = image_info.intern_table_->InternStrongImageString(obj->AsString());
-    }
-    if (obj != interned) {
-      if (!IsImageBinSlotAssigned(interned)) {
-        // interned obj is after us, allocate its location early
-        AssignImageBinSlot(interned);
-      }
-      // point those looking for this object to the interned version.
-      SetImageBinSlot(obj, GetImageBinSlot(interned));
-      return;
-    }
-    // else (obj == interned), nothing to do but fall through to the normal case
-  }
-
-  AssignImageBinSlot(obj);
-}
 
 ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
   Runtime* runtime = Runtime::Current();
@@ -1092,61 +1062,33 @@
   return image_roots.Get();
 }
 
-// Walk instance fields of the given Class. Separate function to allow recursion on the super
-// class.
-void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) {
-  // Visit fields of parent classes first.
-  StackHandleScope<1> hs(Thread::Current());
-  Handle<mirror::Class> h_class(hs.NewHandle(klass));
-  mirror::Class* super = h_class->GetSuperClass();
-  if (super != nullptr) {
-    WalkInstanceFields(obj, super);
+mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack,
+                                              mirror::Object* obj,
+                                              size_t oat_index) {
+  if (obj == nullptr || IsInBootImage(obj)) {
+    // Object is null or already in the image, there is no work to do.
+    return obj;
   }
-  //
-  size_t num_reference_fields = h_class->NumReferenceInstanceFields();
-  MemberOffset field_offset = h_class->GetFirstReferenceInstanceFieldOffset();
-  for (size_t i = 0; i < num_reference_fields; ++i) {
-    mirror::Object* value = obj->GetFieldObject<mirror::Object>(field_offset);
-    if (value != nullptr) {
-      WalkFieldsInOrder(value);
-    }
-    field_offset = MemberOffset(field_offset.Uint32Value() +
-                                sizeof(mirror::HeapReference<mirror::Object>));
-  }
-}
-
-// For an unvisited object, visit it then all its children found via fields.
-void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
-  if (IsInBootImage(obj)) {
-    // Object is in the image, don't need to fix it up.
-    return;
-  }
-  // Use our own visitor routine (instead of GC visitor) to get better locality between
-  // an object and its fields
   if (!IsImageBinSlotAssigned(obj)) {
-    // Walk instance fields of all objects
-    StackHandleScope<2> hs(Thread::Current());
-    Handle<mirror::Object> h_obj(hs.NewHandle(obj));
-    Handle<mirror::Class> klass(hs.NewHandle(obj->GetClass()));
-    // visit the object itself.
-    CalculateObjectBinSlots(h_obj.Get());
-    WalkInstanceFields(h_obj.Get(), klass.Get());
-    // Walk static fields of a Class.
-    if (h_obj->IsClass()) {
-      size_t num_reference_static_fields = klass->NumReferenceStaticFields();
-      MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset(target_ptr_size_);
-      for (size_t i = 0; i < num_reference_static_fields; ++i) {
-        mirror::Object* value = h_obj->GetFieldObject<mirror::Object>(field_offset);
-        if (value != nullptr) {
-          WalkFieldsInOrder(value);
-        }
-        field_offset = MemberOffset(field_offset.Uint32Value() +
-                                    sizeof(mirror::HeapReference<mirror::Object>));
+    // We want to intern all strings but also assign offsets for the source string. Since the
+    // pruning phase has already happened, if we intern a string to one in the image we still
+    // end up copying an unreachable string.
+    if (obj->IsString()) {
+      // Need to check if the string is already interned in another image info so that we don't have
+      // the intern tables of two different images contain the same string.
+      mirror::String* interned = FindInternedString(obj->AsString());
+      if (interned == nullptr) {
+        // Not in another image space, insert to our table.
+        interned = GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString());
+        DCHECK_EQ(interned, obj);
       }
+    } else if (obj->IsDexCache()) {
+      oat_index = GetOatIndexForDexCache(obj->AsDexCache());
+    } else if (obj->IsClass()) {
       // Visit and assign offsets for fields and field arrays.
-      auto* as_klass = h_obj->AsClass();
+      mirror::Class* as_klass = obj->AsClass();
       mirror::DexCache* dex_cache = as_klass->GetDexCache();
-      DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+      DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError);
       if (compile_app_image_) {
         // Extra sanity, no boot loader classes should be left!
         CHECK(!IsBootClassLoaderClass(as_klass)) << PrettyClass(as_klass);
@@ -1154,14 +1096,14 @@
       LengthPrefixedArray<ArtField>* fields[] = {
           as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(),
       };
-      size_t oat_index = GetOatIndexForDexCache(dex_cache);
+      // Overwrite the oat index value since the class' dex cache is more accurate of where it
+      // belongs.
+      oat_index = GetOatIndexForDexCache(dex_cache);
       ImageInfo& image_info = GetImageInfo(oat_index);
       {
-        // Note: This table is only accessed from the image writer, so the lock is technically
-        // unnecessary.
-        WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-        // Insert in the class table for this iamge.
-        image_info.class_table_->Insert(as_klass);
+        // Note: This table is only accessed from the image writer, avoid locking to prevent lock
+        // order violations from root visiting.
+        image_info.class_table_->InsertWithoutLocks(as_klass);
       }
       for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
         // Total array length including header.
@@ -1251,26 +1193,26 @@
         ImTable* imt = as_klass->GetImt(target_ptr_size_);
         TryAssignImTableOffset(imt, oat_index);
       }
-    } else if (h_obj->IsObjectArray()) {
-      // Walk elements of an object array.
-      int32_t length = h_obj->AsObjectArray<mirror::Object>()->GetLength();
-      for (int32_t i = 0; i < length; i++) {
-        mirror::ObjectArray<mirror::Object>* obj_array = h_obj->AsObjectArray<mirror::Object>();
-        mirror::Object* value = obj_array->Get(i);
-        if (value != nullptr) {
-          WalkFieldsInOrder(value);
-        }
-      }
-    } else if (h_obj->IsClassLoader()) {
+    } else if (obj->IsClassLoader()) {
       // Register the class loader if it has a class table.
       // The fake boot class loader should not get registered and we should end up with only one
       // class loader.
-      mirror::ClassLoader* class_loader = h_obj->AsClassLoader();
+      mirror::ClassLoader* class_loader = obj->AsClassLoader();
       if (class_loader->GetClassTable() != nullptr) {
         class_loaders_.insert(class_loader);
       }
     }
+    AssignImageBinSlot(obj, oat_index);
+    work_stack.emplace(obj, oat_index);
   }
+  if (obj->IsString()) {
+    // Always return the interned string if there exists one.
+    mirror::String* interned = FindInternedString(obj->AsString());
+    if (interned != nullptr) {
+      return interned;
+    }
+  }
+  return obj;
 }
 
 bool ImageWriter::NativeRelocationAssigned(void* ptr) const {
@@ -1327,10 +1269,16 @@
   offset += ArtMethod::Size(target_ptr_size_);
 }
 
-void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) {
+void ImageWriter::EnsureBinSlotAssignedCallback(mirror::Object* obj, void* arg) {
   ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg);
   DCHECK(writer != nullptr);
-  writer->WalkFieldsInOrder(obj);
+  if (!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(obj)) {
+    CHECK(writer->IsImageBinSlotAssigned(obj)) << PrettyTypeOf(obj) << " " << obj;
+  }
+}
+
+void ImageWriter::DeflateMonitorCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED) {
+  Monitor::Deflate(Thread::Current(), obj);
 }
 
 void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) {
@@ -1354,6 +1302,88 @@
   AssignImageOffset(obj, bin_slot);
 }
 
+class ImageWriter::VisitReferencesVisitor {
+ public:
+  VisitReferencesVisitor(ImageWriter* image_writer, WorkStack* work_stack, size_t oat_index)
+      : image_writer_(image_writer), work_stack_(work_stack), oat_index_(oat_index) {}
+
+  // Fix up separately since we also need to fix up method entrypoints.
+  ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    root->Assign(VisitReference(root->AsMirrorPtr()));
+  }
+
+  ALWAYS_INLINE void operator() (mirror::Object* obj,
+                                 MemberOffset offset,
+                                 bool is_static ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    mirror::Object* ref =
+        obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+    obj->SetFieldObject</*kTransactionActive*/false>(offset, VisitReference(ref));
+  }
+
+  ALWAYS_INLINE void operator() (mirror::Class* klass ATTRIBUTE_UNUSED,
+                                 mirror::Reference* ref) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    ref->SetReferent</*kTransactionActive*/false>(
+        VisitReference(ref->GetReferent<kWithoutReadBarrier>()));
+  }
+
+ private:
+  mirror::Object* VisitReference(mirror::Object* ref) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return image_writer_->TryAssignBinSlot(*work_stack_, ref, oat_index_);
+  }
+
+  ImageWriter* const image_writer_;
+  WorkStack* const work_stack_;
+  const size_t oat_index_;
+};
+
+class ImageWriter::GetRootsVisitor : public RootVisitor  {
+ public:
+  explicit GetRootsVisitor(std::vector<mirror::Object*>* roots) : roots_(roots) {}
+
+  void VisitRoots(mirror::Object*** roots,
+                  size_t count,
+                  const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    for (size_t i = 0; i < count; ++i) {
+      roots_->push_back(*roots[i]);
+    }
+  }
+
+  void VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
+                  size_t count,
+                  const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    for (size_t i = 0; i < count; ++i) {
+      roots_->push_back(roots[i]->AsMirrorPtr());
+    }
+  }
+
+ private:
+  std::vector<mirror::Object*>* const roots_;
+};
+
+void ImageWriter::ProcessWorkStack(WorkStack* work_stack) {
+  while (!work_stack->empty()) {
+    std::pair<mirror::Object*, size_t> pair(work_stack->top());
+    work_stack->pop();
+    VisitReferencesVisitor visitor(this, work_stack, /*oat_index*/ pair.second);
+    // Walk references and assign bin slots for them.
+    pair.first->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+        visitor,
+        visitor);
+  }
+}
+
 void ImageWriter::CalculateNewObjectOffsets() {
   Thread* const self = Thread::Current();
   StackHandleScopeCollection handles(self);
@@ -1362,8 +1392,8 @@
     image_roots.push_back(handles.NewHandle(CreateImageRoots(i)));
   }
 
-  auto* runtime = Runtime::Current();
-  auto* heap = runtime->GetHeap();
+  Runtime* const runtime = Runtime::Current();
+  gc::Heap* const heap = runtime->GetHeap();
 
   // Leave space for the header, but do not write it yet, we need to
   // know where image_roots is going to end up
@@ -1392,8 +1422,64 @@
     }
   }
 
-  // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
-  heap->VisitObjects(WalkFieldsCallback, this);
+  // Deflate monitors before we visit roots since deflating acquires the monitor lock. Acquiring
+  // this lock while holding other locks may cause lock order violations.
+  heap->VisitObjects(DeflateMonitorCallback, this);
+
+  // Work list of <object, oat_index> for objects. Everything on the stack must already be
+  // assigned a bin slot.
+  WorkStack work_stack;
+
+  // Special case interned strings to put them in the image they are likely to be resolved from.
+  for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
+    auto it = dex_file_oat_index_map_.find(dex_file);
+    DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
+    const size_t oat_index = it->second;
+    InternTable* const intern_table = runtime->GetInternTable();
+    for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) {
+      uint32_t utf16_length;
+      const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(i, &utf16_length);
+      mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data);
+      TryAssignBinSlot(work_stack, string, oat_index);
+    }
+  }
+
+  // Get the GC roots and then visit them separately to avoid lock violations since the root visitor
+  // visits roots while holding various locks.
+  {
+    std::vector<mirror::Object*> roots;
+    GetRootsVisitor root_visitor(&roots);
+    runtime->VisitRoots(&root_visitor);
+    for (mirror::Object* obj : roots) {
+      TryAssignBinSlot(work_stack, obj, GetDefaultOatIndex());
+    }
+  }
+  ProcessWorkStack(&work_stack);
+
+  // For app images, there may be objects that are only held live by the by the boot image. One
+  // example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback
+  // does not fail any checks. TODO: We should probably avoid copying these objects.
+  if (compile_app_image_) {
+    for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) {
+      DCHECK(space->IsImageSpace());
+      gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+      live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+                                    reinterpret_cast<uintptr_t>(space->Limit()),
+                                    [this, &work_stack](mirror::Object* obj)
+          REQUIRES_SHARED(Locks::mutator_lock_) {
+        VisitReferencesVisitor visitor(this, &work_stack, GetDefaultOatIndex());
+        // Visit all references and try to assign bin slots for them (calls TryAssignBinSlot).
+        obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+            visitor,
+            visitor);
+      });
+    }
+    // Process the work stack in case anything was added by TryAssignBinSlot.
+    ProcessWorkStack(&work_stack);
+  }
+
+  // Verify that all objects have assigned image bin slots.
+  heap->VisitObjects(EnsureBinSlotAssignedCallback, this);
 
   // Calculate size of the dex cache arrays slot and prepare offsets.
   PrepareDexCacheArraySlots();
@@ -1989,14 +2075,10 @@
   } else {
     if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) {
       // Need to go update the ArtMethod.
-      auto* dest = down_cast<mirror::AbstractMethod*>(copy);
-      auto* src = down_cast<mirror::AbstractMethod*>(orig);
+      auto* dest = down_cast<mirror::Executable*>(copy);
+      auto* src = down_cast<mirror::Executable*>(orig);
       ArtMethod* src_method = src->GetArtMethod();
-      auto it = native_object_relocations_.find(src_method);
-      CHECK(it != native_object_relocations_.end())
-          << "Missing relocation for AbstractMethod.artMethod " << PrettyMethod(src_method);
-      dest->SetArtMethod(
-          reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset));
+      dest->SetArtMethod(GetImageMethodAddress(src_method));
     } else if (!klass->IsArrayClass()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
@@ -2285,25 +2367,21 @@
 }
 
 size_t ImageWriter::GetOatIndex(mirror::Object* obj) const {
-  if (compile_app_image_) {
+  if (!IsMultiImage()) {
     return GetDefaultOatIndex();
-  } else {
-    mirror::DexCache* dex_cache =
-        obj->IsDexCache() ? obj->AsDexCache()
-                          : obj->IsClass() ? obj->AsClass()->GetDexCache()
-                                           : obj->GetClass()->GetDexCache();
-    return GetOatIndexForDexCache(dex_cache);
   }
+  auto it = oat_index_map_.find(obj);
+  DCHECK(it != oat_index_map_.end());
+  return it->second;
 }
 
 size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const {
-  if (compile_app_image_) {
+  if (!IsMultiImage()) {
     return GetDefaultOatIndex();
-  } else {
-    auto it = dex_file_oat_index_map_.find(dex_file);
-    DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
-    return it->second;
   }
+  auto it = dex_file_oat_index_map_.find(dex_file);
+  DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
+  return it->second;
 }
 
 size_t ImageWriter::GetOatIndexForDexCache(mirror::DexCache* dex_cache) const {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 76749cf..acd1681 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -23,6 +23,7 @@
 #include <cstddef>
 #include <memory>
 #include <set>
+#include <stack>
 #include <string>
 #include <ostream>
 
@@ -144,6 +145,8 @@
   void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header);
 
  private:
+  using WorkStack = std::stack<std::pair<mirror::Object*, size_t>>;
+
   bool AllocMemory();
 
   // Mark the objects defined in this space in the given live bitmap.
@@ -321,7 +324,10 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void PrepareDexCacheArraySlots() REQUIRES_SHARED(Locks::mutator_lock_);
-  void AssignImageBinSlot(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_);
+  void AssignImageBinSlot(mirror::Object* object, size_t oat_index)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  mirror::Object* TryAssignBinSlot(WorkStack& work_stack, mirror::Object* obj, size_t oat_index)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
       REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsImageBinSlotAssigned(mirror::Object* object) const
@@ -378,6 +384,8 @@
   // Lays out where the image objects will be at runtime.
   void CalculateNewObjectOffsets()
       REQUIRES_SHARED(Locks::mutator_lock_);
+  void ProcessWorkStack(WorkStack* work_stack)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   void CreateHeader(size_t oat_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
   mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
@@ -387,11 +395,9 @@
   void UnbinObjectsIntoOffset(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass)
+  static void EnsureBinSlotAssignedCallback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void WalkFieldsInOrder(mirror::Object* obj)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void WalkFieldsCallback(mirror::Object* obj, void* arg)
+  static void DeflateMonitorCallback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -461,6 +467,10 @@
                                   std::unordered_set<mirror::Class*>* visited)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  bool IsMultiImage() const {
+    return image_infos_.size() > 1;
+  }
+
   static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
 
   uintptr_t NativeOffsetInImage(void* obj) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -519,6 +529,9 @@
   // forwarding addresses as well as copying over hash codes.
   std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_;
 
+  // Oat index map for objects.
+  std::unordered_map<mirror::Object*, uint32_t> oat_index_map_;
+
   // Boolean flags.
   const bool compile_pic_;
   const bool compile_app_image_;
@@ -573,8 +586,10 @@
   friend class FixupClassVisitor;
   friend class FixupRootVisitor;
   friend class FixupVisitor;
+  class GetRootsVisitor;
   friend class NativeLocationVisitor;
   friend class NonImageClassesVisitor;
+  class VisitReferencesVisitor;
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
 };
 
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.h b/compiler/intrinsics_enum.h
similarity index 61%
copy from runtime/native/java_lang_reflect_AbstractMethod.h
copy to compiler/intrinsics_enum.h
index 222e5a0..5528181 100644
--- a/runtime/native/java_lang_reflect_AbstractMethod.h
+++ b/compiler/intrinsics_enum.h
@@ -14,15 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-
-#include <jni.h>
+#ifndef ART_COMPILER_INTRINSICS_ENUM_H_
+#define ART_COMPILER_INTRINSICS_ENUM_H_
 
 namespace art {
 
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env);
+enum class Intrinsics {
+#define OPTIMIZING_INTRINSICS(Name, ...) \
+  k ## Name,
+#include "intrinsics_list.h"
+  kNone,
+  INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+};
+std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic);
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#endif  // ART_COMPILER_INTRINSICS_ENUM_H_
diff --git a/compiler/intrinsics_list.h b/compiler/intrinsics_list.h
new file mode 100644
index 0000000..5877f57
--- /dev/null
+++ b/compiler/intrinsics_list.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C, "", "", "") 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License", "", "", "");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_COMPILER_INTRINSICS_LIST_H_
+#define ART_COMPILER_INTRINSICS_LIST_H_
+
+// All intrinsics supported by ART. Format is name, then whether it is expected
+// to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual), then whether it requires an
+// environment, may have side effects, or may throw exceptions.
+
+// Note: adding a new intrinsic requires an art image version change,
+// as the modifiers flag for some ArtMethods will need to be changed.
+
+#define INTRINSICS_LIST(V) \
+  V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J") \
+  V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "doubleToLongBits", "(D)J") \
+  V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "isInfinite", "(D)Z") \
+  V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "isNaN", "(D)Z") \
+  V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Double;", "longBitsToDouble", "(J)D") \
+  V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Float;", "floatToRawIntBits", "(F)I") \
+  V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Float;", "floatToIntBits", "(F)I") \
+  V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Float;", "isInfinite", "(F)Z") \
+  V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Float;", "isNaN", "(F)Z") \
+  V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Float;", "intBitsToFloat", "(I)F") \
+  V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "reverse", "(I)I") \
+  V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "reverseBytes", "(I)I") \
+  V(IntegerBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "bitCount", "(I)I") \
+  V(IntegerCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "compare", "(II)I") \
+  V(IntegerHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "highestOneBit", "(I)I") \
+  V(IntegerLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "lowestOneBit", "(I)I") \
+  V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "numberOfLeadingZeros", "(I)I") \
+  V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "numberOfTrailingZeros", "(I)I") \
+  V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "rotateRight", "(II)I") \
+  V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "rotateLeft", "(II)I") \
+  V(IntegerSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Integer;", "signum", "(I)I") \
+  V(LongReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "reverse", "(J)J") \
+  V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "reverseBytes", "(J)J") \
+  V(LongBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "bitCount", "(J)I") \
+  V(LongCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "compare", "(JJ)I") \
+  V(LongHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "highestOneBit", "(J)J") \
+  V(LongLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "lowestOneBit", "(J)J") \
+  V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "numberOfLeadingZeros", "(J)I") \
+  V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "numberOfTrailingZeros", "(J)I") \
+  V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "rotateRight", "(JI)J") \
+  V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "rotateLeft", "(JI)J") \
+  V(LongSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Long;", "signum", "(J)I") \
+  V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Short;", "reverseBytes", "(S)S") \
+  V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "abs", "(D)D") \
+  V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "abs", "(F)F") \
+  V(MathAbsLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "abs", "(J)J") \
+  V(MathAbsInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "abs", "(I)I") \
+  V(MathMinDoubleDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "min", "(DD)D") \
+  V(MathMinFloatFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "min", "(FF)F") \
+  V(MathMinLongLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "min", "(JJ)J") \
+  V(MathMinIntInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "min", "(II)I") \
+  V(MathMaxDoubleDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "max", "(DD)D") \
+  V(MathMaxFloatFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "max", "(FF)F") \
+  V(MathMaxLongLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "max", "(JJ)J") \
+  V(MathMaxIntInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "max", "(II)I") \
+  V(MathCos, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "cos", "(D)D") \
+  V(MathSin, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "sin", "(D)D") \
+  V(MathAcos, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "acos", "(D)D") \
+  V(MathAsin, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "asin", "(D)D") \
+  V(MathAtan, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "atan", "(D)D") \
+  V(MathAtan2, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "atan2", "(DD)D") \
+  V(MathCbrt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "cbrt", "(D)D") \
+  V(MathCosh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "cosh", "(D)D") \
+  V(MathExp, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "exp", "(D)D") \
+  V(MathExpm1, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "expm1", "(D)D") \
+  V(MathHypot, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "hypot", "(DD)D") \
+  V(MathLog, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "log", "(D)D") \
+  V(MathLog10, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "log10", "(D)D") \
+  V(MathNextAfter, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "nextAfter", "(DD)D") \
+  V(MathSinh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "sinh", "(D)D") \
+  V(MathTan, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "tan", "(D)D") \
+  V(MathTanh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "tanh", "(D)D") \
+  V(MathSqrt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "sqrt", "(D)D") \
+  V(MathCeil, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "ceil", "(D)D") \
+  V(MathFloor, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "floor", "(D)D") \
+  V(MathRint, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "rint", "(D)D") \
+  V(MathRoundDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "round", "(D)J") \
+  V(MathRoundFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "round", "(F)I") \
+  V(SystemArrayCopyChar, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/System;", "arraycopy", "([CI[CII)V") \
+  V(SystemArrayCopy, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/System;", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V") \
+  V(ThreadCurrentThread, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Ljava/lang/Thread;", "currentThread", "()Ljava/lang/Thread;") \
+  V(MemoryPeekByte, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Llibcore/io/Memory;", "peekByte", "(J)B") \
+  V(MemoryPeekIntNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Llibcore/io/Memory;", "peekIntNative", "(J)I") \
+  V(MemoryPeekLongNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Llibcore/io/Memory;", "peekLongNative", "(J)J") \
+  V(MemoryPeekShortNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Llibcore/io/Memory;", "peekShortNative", "(J)S") \
+  V(MemoryPokeByte, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeByte", "(JB)V") \
+  V(MemoryPokeIntNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeIntNative", "(JI)V") \
+  V(MemoryPokeLongNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeLongNative", "(JJ)V") \
+  V(MemoryPokeShortNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeShortNative", "(JS)V") \
+  V(StringCharAt, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "charAt", "(I)C") \
+  V(StringCompareTo, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I") \
+  V(StringEquals, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z") \
+  V(StringGetCharsNoCheck, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "getCharsNoCheck", "(II[CI)V") \
+  V(StringIndexOf, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "indexOf", "(I)I") \
+  V(StringIndexOfAfter, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "indexOf", "(II)I") \
+  V(StringIsEmpty, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/String;", "isEmpty", "()Z") \
+  V(StringLength, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/String;", "length", "()I") \
+  V(StringNewStringFromBytes, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromBytes", "([BIII)Ljava/lang/String;") \
+  V(StringNewStringFromChars, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromChars", "(II[C)Ljava/lang/String;") \
+  V(StringNewStringFromString, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;") \
+  V(UnsafeCASInt, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapInt", "(Ljava/lang/Object;JII)Z") \
+  V(UnsafeCASLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z") \
+  V(UnsafeCASObject, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z") \
+  V(UnsafeGet, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getInt", "(Ljava/lang/Object;J)I") \
+  V(UnsafeGetVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getIntVolatile", "(Ljava/lang/Object;J)I") \
+  V(UnsafeGetObject, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;") \
+  V(UnsafeGetObjectVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;") \
+  V(UnsafeGetLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getLong", "(Ljava/lang/Object;J)J") \
+  V(UnsafeGetLongVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getLongVolatile", "(Ljava/lang/Object;J)J") \
+  V(UnsafePut, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putInt", "(Ljava/lang/Object;JI)V") \
+  V(UnsafePutOrdered, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putOrderedInt", "(Ljava/lang/Object;JI)V") \
+  V(UnsafePutVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putIntVolatile", "(Ljava/lang/Object;JI)V") \
+  V(UnsafePutObject, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V") \
+  V(UnsafePutObjectOrdered, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putOrderedObject", "(Ljava/lang/Object;JLjava/lang/Object;)V") \
+  V(UnsafePutObjectVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V") \
+  V(UnsafePutLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putLong", "(Ljava/lang/Object;JJ)V") \
+  V(UnsafePutLongOrdered, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putOrderedLong", "(Ljava/lang/Object;JJ)V") \
+  V(UnsafePutLongVolatile, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "putLongVolatile", "(Ljava/lang/Object;JJ)V") \
+  V(UnsafeGetAndAddInt, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getAndAddInt", "(Ljava/lang/Object;JI)I") \
+  V(UnsafeGetAndAddLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getAndAddLong", "(Ljava/lang/Object;JJ)J") \
+  V(UnsafeGetAndSetInt, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getAndSetInt", "(Ljava/lang/Object;JI)I") \
+  V(UnsafeGetAndSetLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getAndSetLong", "(Ljava/lang/Object;JJ)J") \
+  V(UnsafeGetAndSetObject, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "getAndSetObject", "(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;") \
+  V(UnsafeLoadFence, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "loadFence", "()V") \
+  V(UnsafeStoreFence, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "storeFence", "()V") \
+  V(UnsafeFullFence, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "fullFence", "()V") \
+  V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/ref/Reference;", "getReferent", "()Ljava/lang/Object;")
+
+#endif  // ART_COMPILER_INTRINSICS_LIST_H_
+#undef ART_COMPILER_INTRINSICS_LIST_H_   // #define is only for lint.
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 7246ace..4f86905 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -150,11 +150,9 @@
     instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines());
   }
   cumulative_logger_.reset(new CumulativeLogger("jit times"));
-  method_inliner_map_.reset(new DexFileToMethodInlinerMap);
   compiler_driver_.reset(new CompilerDriver(
       compiler_options_.get(),
       /* verification_results */ nullptr,
-      method_inliner_map_.get(),
       Compiler::kOptimizing,
       instruction_set,
       instruction_set_features_.get(),
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index 18e3155..ea2747c 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -19,7 +19,6 @@
 
 #include "base/mutex.h"
 #include "compiled_method.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 
@@ -49,7 +48,6 @@
  private:
   std::unique_ptr<CompilerOptions> compiler_options_;
   std::unique_ptr<CumulativeLogger> cumulative_logger_;
-  std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_;
   std::unique_ptr<CompilerDriver> compiler_driver_;
   std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
   std::unique_ptr<File> perf_file_;
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index cdd4c68..19d55a3 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -36,7 +36,7 @@
 #include "nativeloader/native_loader.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_bar(JNIEnv*, jobject, jint count) {
@@ -238,7 +238,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::ClassLoader> loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
     // Compile the native method before starting the runtime
     mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
     const auto pointer_size = class_linker_->GetImagePointerSize();
@@ -391,12 +391,12 @@
 // 3) synchronized keyword
 // -- TODO: We can support (1) if we remove the mutator lock assert during stub lookup.
 # define JNI_TEST_NORMAL_ONLY(TestName)          \
-  TEST_F(JniCompilerTest, TestName ## Default) { \
+  TEST_F(JniCompilerTest, TestName ## NormalCompiler) { \
     SCOPED_TRACE("Normal JNI with compiler");    \
     gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TestName ## Impl();                          \
   }                                              \
-  TEST_F(JniCompilerTest, TestName ## Generic) { \
+  TEST_F(JniCompilerTest, TestName ## NormalGeneric) { \
     SCOPED_TRACE("Normal JNI with generic");     \
     gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TEST_DISABLED_FOR_MIPS();                    \
@@ -404,46 +404,42 @@
     TestName ## Impl();                          \
   }
 
-// Test normal compiler, @FastNative compiler, and normal/@FastNative generic for normal natives.
+// Test (normal, @FastNative) x (compiler, generic).
 #define JNI_TEST(TestName) \
   JNI_TEST_NORMAL_ONLY(TestName)                 \
-  TEST_F(JniCompilerTest, TestName ## Fast) {    \
+  TEST_F(JniCompilerTest, TestName ## FastCompiler) {    \
     SCOPED_TRACE("@FastNative JNI with compiler");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
     TestName ## Impl();                          \
   }                                              \
-                                          \
-
-// TODO: maybe. @FastNative generic JNI support?
-#if 0
+                                                 \
   TEST_F(JniCompilerTest, TestName ## FastGeneric) { \
+    SCOPED_TRACE("@FastNative JNI with generic");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
     TEST_DISABLED_FOR_MIPS();                    \
     SetCheckGenericJni(true);                    \
     TestName ## Impl();                          \
   }
-#endif
 
+// Test (@CriticalNative) x (compiler, generic) only.
 #define JNI_TEST_CRITICAL_ONLY(TestName) \
-  TEST_F(JniCompilerTest, TestName ## DefaultCritical) { \
+  TEST_F(JniCompilerTest, TestName ## CriticalCompiler) { \
     SCOPED_TRACE("@CriticalNative JNI with compiler");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
     TestName ## Impl();                          \
+  }                                              \
+  TEST_F(JniCompilerTest, TestName ## CriticalGeneric) { \
+    SCOPED_TRACE("@CriticalNative JNI with generic");  \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
+    SetCheckGenericJni(true);                    \
+    TestName ## Impl();                          \
   }
 
-// Test everything above and also the @CriticalNative compiler, and @CriticalNative generic JNI.
+// Test everything: (normal, @FastNative, @CriticalNative) x (compiler, generic).
 #define JNI_TEST_CRITICAL(TestName)              \
   JNI_TEST(TestName)                             \
   JNI_TEST_CRITICAL_ONLY(TestName)               \
 
-// TODO: maybe, more likely since calling convention changed. @Criticalnative generic JNI support?
-#if 0
-  TEST_F(JniCompilerTest, TestName ## GenericCritical) { \
-    gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
-    TestName ## Impl();                          \
-  }
-#endif
-
 static void expectValidThreadState() {
   // Normal JNI always transitions to "Native". Other JNIs stay in the "Runnable" state.
   if (IsCurrentJniNormal()) {
@@ -506,6 +502,7 @@
 // Temporarily disable the EXPECT_NUM_STACK_REFERENCES check (for a single test).
 struct ScopedDisableCheckNumStackReferences {
   ScopedDisableCheckNumStackReferences() {
+    CHECK(sCheckNumStackReferences);  // No nested support.
     sCheckNumStackReferences = false;
   }
 
@@ -1143,8 +1140,8 @@
     // Build stack trace
     jobject internal = Thread::Current()->CreateInternalStackTrace<false>(soa);
     jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
-    mirror::ObjectArray<mirror::StackTraceElement>* trace_array =
-        soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array);
+    ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> trace_array =
+        soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(ste_array);
     EXPECT_TRUE(trace_array != nullptr);
     EXPECT_EQ(11, trace_array->GetLength());
 
@@ -1208,7 +1205,7 @@
   // Add 10 local references
   ScopedObjectAccess soa(env);
   for (int i = 0; i < 10; i++) {
-    soa.AddLocalReference<jobject>(soa.Decode<mirror::Object*>(thisObj));
+    soa.AddLocalReference<jobject>(soa.Decode<mirror::Object>(thisObj));
   }
   return x+1;
 }
@@ -1286,7 +1283,7 @@
 
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
-  EXPECT_TRUE(self->HoldsLock(soa.Decode<mirror::Object*>(thisObj)));
+  EXPECT_TRUE(self->HoldsLock(soa.Decode<mirror::Object>(thisObj).Decode()));
   return nullptr;
 }
 
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 304b31c..62b3a0a 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -22,7 +22,6 @@
 #include "base/array_ref.h"
 #include "base/macros.h"
 #include "compiled_method.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
@@ -43,10 +42,8 @@
   RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
       : compiler_options_(),
         verification_results_(&compiler_options_),
-        inliner_map_(),
         driver_(&compiler_options_,
                 &verification_results_,
-                &inliner_map_,
                 Compiler::kQuick,
                 instruction_set,
                 /* instruction_set_features*/ nullptr,
@@ -269,7 +266,6 @@
 
   CompilerOptions compiler_options_;
   VerificationResults verification_results_;
-  DexFileToMethodInlinerMap inliner_map_;
   CompilerDriver driver_;  // Needed for constructing CompiledMethod.
   std::string error_msg_;
   InstructionSet instruction_set_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 24d102d..e8bc67d 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -23,7 +23,6 @@
 #include "compiled_method.h"
 #include "compiler.h"
 #include "debug/method_debug_info.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
@@ -38,7 +37,7 @@
 #include "mirror/object_array-inl.h"
 #include "oat_file-inl.h"
 #include "oat_writer.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utils/test_dex_file_builder.h"
 
 namespace art {
@@ -100,15 +99,12 @@
       compiler_options_->ParseCompilerOption(option, Usage);
     }
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
-    method_inliner_map_.reset(new DexFileToMethodInlinerMap);
     callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
-                                                method_inliner_map_.get(),
                                                 CompilerCallbacks::CallbackMode::kCompileApp));
     Runtime::Current()->SetCompilerCallbacks(callbacks_.get());
     timer_.reset(new CumulativeLogger("Compilation times"));
     compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                               verification_results_.get(),
-                                              method_inliner_map_.get(),
                                               compiler_kind,
                                               insn_set,
                                               insn_features_.get(),
@@ -501,7 +497,8 @@
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   for (const DexFile* dex_file : dex_files) {
     ScopedObjectAccess soa(Thread::Current());
-    class_linker->RegisterDexFile(*dex_file, soa.Decode<mirror::ClassLoader*>(class_loader));
+    class_linker->RegisterDexFile(*dex_file,
+                                  soa.Decode<mirror::ClassLoader>(class_loader).Decode());
   }
   compiler_driver_->SetDexFilesForOatFile(dex_files);
   compiler_driver_->CompileAll(class_loader, dex_files, &timings);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index d629c0c..54ec7c1 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -50,11 +50,12 @@
 #include "oat_quick_method_header.h"
 #include "os.h"
 #include "safe_map.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "type_lookup_table.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "vdex_file.h"
 #include "verifier/method_verifier.h"
+#include "verifier/verifier_deps.h"
 #include "zip_archive.h"
 
 namespace art {
@@ -297,6 +298,7 @@
     dex_files_(nullptr),
     vdex_size_(0u),
     vdex_dex_files_offset_(0u),
+    vdex_verifier_deps_offset_(0u),
     oat_size_(0u),
     bss_size_(0u),
     oat_data_offset_(0u),
@@ -307,6 +309,8 @@
     size_oat_header_(0),
     size_oat_header_key_value_store_(0),
     size_dex_file_(0),
+    size_verifier_deps_(0),
+    size_verifier_deps_alignment_(0),
     size_interpreter_to_interpreter_bridge_(0),
     size_interpreter_to_compiled_code_bridge_(0),
     size_jni_dlsym_lookup_(0),
@@ -476,11 +480,6 @@
         !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
       return false;
     }
-
-    // VDEX is finalized. Seek to the beginning of the file and write the header.
-    if (!WriteVdexHeader(vdex_out.get())) {
-      return false;
-    }
   } else {
     // Write DEX files into OAT, mmap and open them.
     if (!WriteDexFiles(oat_rodata, vdex_file) ||
@@ -967,7 +966,7 @@
           nullptr,
           invoke_type);
       if (method == nullptr) {
-        LOG(INTERNAL_FATAL) << "Unexpected failure to resolve a method: "
+        LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
             << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
         soa.Self()->AssertPendingException();
         mirror::Throwable* exc = soa.Self()->GetException();
@@ -1595,6 +1594,52 @@
   return true;
 }
 
+bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) {
+  if (!kIsVdexEnabled) {
+    return true;
+  }
+
+  if (verifier_deps == nullptr) {
+    // Nothing to write. Record the offset, but no need
+    // for alignment.
+    vdex_verifier_deps_offset_ = vdex_size_;
+    return true;
+  }
+
+  size_t initial_offset = vdex_size_;
+  size_t start_offset = RoundUp(initial_offset, 4u);
+
+  vdex_size_ = start_offset;
+  vdex_verifier_deps_offset_ = vdex_size_;
+  size_verifier_deps_alignment_ = start_offset - initial_offset;
+
+  off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet);
+  if (actual_offset != static_cast<off_t>(start_offset)) {
+    PLOG(ERROR) << "Failed to seek to verifier deps section. Actual: " << actual_offset
+                << " Expected: " << start_offset
+                << " Output: " << vdex_out->GetLocation();
+    return false;
+  }
+
+  std::vector<uint8_t> buffer;
+  verifier_deps->Encode(&buffer);
+
+  if (!vdex_out->WriteFully(buffer.data(), buffer.size())) {
+    PLOG(ERROR) << "Failed to write verifier deps."
+                << " File: " << vdex_out->GetLocation();
+    return false;
+  }
+  if (!vdex_out->Flush()) {
+    PLOG(ERROR) << "Failed to flush stream after writing verifier deps."
+                << " File: " << vdex_out->GetLocation();
+    return false;
+  }
+
+  size_verifier_deps_ = buffer.size();
+  vdex_size_ += size_verifier_deps_;
+  return true;
+}
+
 bool OatWriter::WriteCode(OutputStream* out) {
   CHECK(write_state_ == WriteState::kWriteText);
 
@@ -1638,6 +1683,8 @@
     DO_STAT(size_oat_header_);
     DO_STAT(size_oat_header_key_value_store_);
     DO_STAT(size_dex_file_);
+    DO_STAT(size_verifier_deps_);
+    DO_STAT(size_verifier_deps_alignment_);
     DO_STAT(size_interpreter_to_interpreter_bridge_);
     DO_STAT(size_interpreter_to_compiled_code_bridge_);
     DO_STAT(size_jni_dlsym_lookup_);
@@ -2341,6 +2388,9 @@
 }
 
 bool OatWriter::WriteVdexHeader(OutputStream* vdex_out) {
+  if (!kIsVdexEnabled) {
+    return true;
+  }
   off_t actual_offset = vdex_out->Seek(0, kSeekSet);
   if (actual_offset != 0) {
     PLOG(ERROR) << "Failed to seek to the beginning of vdex file. Actual: " << actual_offset
@@ -2348,12 +2398,24 @@
     return false;
   }
 
-  VdexFile::Header vdex_header;
+  DCHECK_NE(vdex_dex_files_offset_, 0u);
+  DCHECK_NE(vdex_verifier_deps_offset_, 0u);
+
+  size_t dex_section_size = vdex_verifier_deps_offset_ - vdex_dex_files_offset_;
+  size_t verifier_deps_section_size = vdex_size_ - vdex_verifier_deps_offset_;
+
+  VdexFile::Header vdex_header(dex_section_size, verifier_deps_section_size);
   if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) {
     PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation();
     return false;
   }
 
+  if (!vdex_out->Flush()) {
+    PLOG(ERROR) << "Failed to flush stream after writing to vdex file."
+                << " File: " << vdex_out->GetLocation();
+    return false;
+  }
+
   return true;
 }
 
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index dd7d699..670accb 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -50,6 +50,10 @@
 class MultiOatRelativePatcher;
 }  // namespace linker
 
+namespace verifier {
+  class VerifierDeps;
+}  // namespace verifier
+
 // OatHeader         variable length with count of D OatDexFiles
 //
 // OatDexFile[0]     one variable sized OatDexFile with offsets to Dex and OatClasses
@@ -149,6 +153,9 @@
                             bool verify,
                             /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
                             /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
+  bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps);
+  bool WriteVdexHeader(OutputStream* vdex_out);
+
   // Prepare layout of remaining data.
   void PrepareLayout(const CompilerDriver* compiler,
                      ImageWriter* image_writer,
@@ -232,8 +239,6 @@
   // with a given DexMethodVisitor.
   bool VisitDexMethods(DexMethodVisitor* visitor);
 
-  bool WriteVdexHeader(OutputStream* vdex_out);
-
   bool WriteDexFiles(OutputStream* out, File* file);
   bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
   bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
@@ -311,6 +316,9 @@
   // Offset of section holding Dex files inside Vdex.
   size_t vdex_dex_files_offset_;
 
+  // Offset of section holding VerifierDeps inside Vdex.
+  size_t vdex_verifier_deps_offset_;
+
   // Size required for Oat data structures.
   size_t oat_size_;
 
@@ -341,6 +349,8 @@
   uint32_t size_oat_header_;
   uint32_t size_oat_header_key_value_store_;
   uint32_t size_dex_file_;
+  uint32_t size_verifier_deps_;
+  uint32_t size_verifier_deps_alignment_;
   uint32_t size_interpreter_to_interpreter_bridge_;
   uint32_t size_interpreter_to_compiled_code_bridge_;
   uint32_t size_jni_dlsym_lookup_;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 51ba187..0f8cdbb 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -18,6 +18,7 @@
 
 #ifdef ART_ENABLE_CODEGEN_arm
 #include "code_generator_arm.h"
+#include "code_generator_arm_vixl.h"
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -345,7 +346,7 @@
 
   // Initialize to anything to silent compiler warnings.
   QuickEntrypointEnum entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck;
-  switch (invoke->GetOriginalInvokeType()) {
+  switch (invoke->GetInvokeType()) {
     case kStatic:
       entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck;
       break;
@@ -575,11 +576,19 @@
 #ifdef ART_ENABLE_CODEGEN_arm
     case kArm:
     case kThumb2: {
-      return std::unique_ptr<CodeGenerator>(
-          new (arena) arm::CodeGeneratorARM(graph,
-                                            *isa_features.AsArmInstructionSetFeatures(),
-                                            compiler_options,
-                                            stats));
+      if (kArmUseVIXL32) {
+        return std::unique_ptr<CodeGenerator>(
+            new (arena) arm::CodeGeneratorARMVIXL(graph,
+                                                  *isa_features.AsArmInstructionSetFeatures(),
+                                                  compiler_options,
+                                                  stats));
+      } else {
+          return std::unique_ptr<CodeGenerator>(
+            new (arena) arm::CodeGeneratorARM(graph,
+                                              *isa_features.AsArmInstructionSetFeatures(),
+                                              compiler_options,
+                                              stats));
+      }
     }
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -1117,7 +1126,8 @@
   }
 }
 
-LocationSummary* CodeGenerator::CreateNullCheckLocations(HNullCheck* null_check) {
+LocationSummary* CodeGenerator::CreateThrowingSlowPathLocations(HInstruction* instruction,
+                                                                RegisterSet caller_saves) {
   // Note: Using kNoCall allows the method to be treated as leaf (and eliminate the
   // HSuspendCheck from entry block). However, it will still get a valid stack frame
   // because the HNullCheck needs an environment.
@@ -1125,16 +1135,15 @@
   // When throwing from a try block, we may need to retrieve dalvik registers from
   // physical registers and we also need to set up stack mask for GC. This is
   // implicitly achieved by passing kCallOnSlowPath to the LocationSummary.
-  bool can_throw_into_catch_block = null_check->CanThrowIntoCatchBlock();
+  bool can_throw_into_catch_block = instruction->CanThrowIntoCatchBlock();
   if (can_throw_into_catch_block) {
     call_kind = LocationSummary::kCallOnSlowPath;
   }
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(null_check, call_kind);
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   if (can_throw_into_catch_block && compiler_options_.GetImplicitNullChecks()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(caller_saves);  // Default: no caller-save registers.
   }
-  locations->SetInAt(0, Location::RequiresRegister());
-  DCHECK(!null_check->HasUses());
+  DCHECK(!instruction->HasUses());
   return locations;
 }
 
@@ -1273,7 +1282,7 @@
   }
 
   const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
-  for (size_t i : LowToHighBits(fp_spills)) {
+  for (uint32_t i : LowToHighBits(fp_spills)) {
     DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
     saved_fpu_stack_offsets_[i] = stack_offset;
@@ -1292,7 +1301,7 @@
   }
 
   const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
-  for (size_t i : LowToHighBits(fp_spills)) {
+  for (uint32_t i : LowToHighBits(fp_spills)) {
     DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
     stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, i);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 22b5c9c..8500204 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -313,7 +313,8 @@
 
   bool CanMoveNullCheckToUser(HNullCheck* null_check);
   void MaybeRecordImplicitNullCheck(HInstruction* instruction);
-  LocationSummary* CreateNullCheckLocations(HNullCheck* null_check);
+  LocationSummary* CreateThrowingSlowPathLocations(
+      HInstruction* instruction, RegisterSet caller_saves = RegisterSet::Empty());
   void GenerateNullCheck(HNullCheck* null_check);
   virtual void GenerateImplicitNullCheck(HNullCheck* null_check) = 0;
   virtual void GenerateExplicitNullCheck(HNullCheck* null_check) = 0;
@@ -514,7 +515,7 @@
   // otherwise return a fall-back info that should be used instead.
   virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) = 0;
+      HInvokeStaticOrDirect* invoke) = 0;
 
   // Generate a call to a static or direct method.
   virtual void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) = 0;
@@ -579,6 +580,7 @@
         core_spill_mask_(0),
         fpu_spill_mask_(0),
         first_register_slot_in_slow_path_(0),
+        allocated_registers_(RegisterSet::Empty()),
         blocked_core_registers_(graph->GetArena()->AllocArray<bool>(number_of_core_registers,
                                                                     kArenaAllocCodeGenerator)),
         blocked_fpu_registers_(graph->GetArena()->AllocArray<bool>(number_of_fpu_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 3b2758b..681988d 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -191,7 +191,7 @@
 
   uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
   orig_offset = stack_offset;
-  for (size_t i : LowToHighBits(fp_spills)) {
+  for (uint32_t i : LowToHighBits(fp_spills)) {
     DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
     saved_fpu_stack_offsets_[i] = stack_offset;
     stack_offset += kArmWordSize;
@@ -275,10 +275,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -1681,7 +1677,7 @@
 void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -3264,14 +3260,8 @@
 }
 
 void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -4110,7 +4100,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
 
@@ -4430,7 +4420,8 @@
 }
 
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
-  codegen_->CreateNullCheckLocations(instruction);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
 }
 
 void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -4582,7 +4573,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -4598,7 +4589,9 @@
   }
   // We need a temporary register for the read barrier marking slow
   // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
-  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+  // Also need for String compression feature.
+  if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
+      || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
     locations->AddTemp(Location::RequiresRegister());
   }
 }
@@ -4611,6 +4604,8 @@
   Location out_loc = locations->Out();
   uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
   Primitive::Type type = instruction->GetType();
+  const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+                                        instruction->IsStringCharAt();
   HInstruction* array_instr = instruction->GetArray();
   bool has_intermediate_address = array_instr->IsIntermediateAddress();
   // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
@@ -4624,10 +4619,31 @@
     case Primitive::kPrimInt: {
       if (index.IsConstant()) {
         int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
-        uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
+        if (maybe_compressed_char_at) {
+          Register length = IP;
+          Label uncompressed_load, done;
+          uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+          __ LoadFromOffset(kLoadWord, length, obj, count_offset);
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
+          __ cmp(length, ShifterOperand(0));
+          __ b(&uncompressed_load, GE);
+          __ LoadFromOffset(kLoadUnsignedByte,
+                            out_loc.AsRegister<Register>(),
+                            obj,
+                            data_offset + const_index);
+          __ b(&done);
+          __ Bind(&uncompressed_load);
+          __ LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar),
+                            out_loc.AsRegister<Register>(),
+                            obj,
+                            data_offset + (const_index << 1));
+          __ Bind(&done);
+        } else {
+          uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
 
-        LoadOperandType load_type = GetLoadOperandType(type);
-        __ LoadFromOffset(load_type, out_loc.AsRegister<Register>(), obj, full_offset);
+          LoadOperandType load_type = GetLoadOperandType(type);
+          __ LoadFromOffset(load_type, out_loc.AsRegister<Register>(), obj, full_offset);
+        }
       } else {
         Register temp = IP;
 
@@ -4643,7 +4659,24 @@
         } else {
           __ add(temp, obj, ShifterOperand(data_offset));
         }
-        codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
+        if (maybe_compressed_char_at) {
+          Label uncompressed_load, done;
+          uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+          Register length = locations->GetTemp(0).AsRegister<Register>();
+          __ LoadFromOffset(kLoadWord, length, obj, count_offset);
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
+          __ cmp(length, ShifterOperand(0));
+          __ b(&uncompressed_load, GE);
+          __ ldrb(out_loc.AsRegister<Register>(),
+                  Address(temp, index.AsRegister<Register>(), Shift::LSL, 0));
+          __ b(&done);
+          __ Bind(&uncompressed_load);
+          __ ldrh(out_loc.AsRegister<Register>(),
+                  Address(temp, index.AsRegister<Register>(), Shift::LSL, 1));
+          __ Bind(&done);
+        } else {
+          codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>());
+        }
       }
       break;
     }
@@ -4743,7 +4776,7 @@
   if (type == Primitive::kPrimNot) {
     // Potential implicit null checks, in the case of reference
     // arrays, are handled in the previous switch statement.
-  } else {
+  } else if (!maybe_compressed_char_at) {
     codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 }
@@ -5033,6 +5066,10 @@
   Register out = locations->Out().AsRegister<Register>();
   __ LoadFromOffset(kLoadWord, out, obj, offset);
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out compression flag from String's array length.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ bic(out, out, ShifterOperand(1u << 31));
+  }
 }
 
 void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) {
@@ -5067,15 +5104,13 @@
 }
 
 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -5119,7 +5154,7 @@
 void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5441,7 +5476,7 @@
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
 
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
@@ -5745,7 +5780,7 @@
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   if (baker_read_barrier_slow_path) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
@@ -6683,7 +6718,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) {
+      HInvokeStaticOrDirect* invoke) {
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
   // We disable pc-relative load when there is an irreducible loop, as the optimization
   // is incompatible with it.
@@ -6697,7 +6732,7 @@
 
   if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
     const DexFile& outer_dex_file = GetGraph()->GetDexFile();
-    if (&outer_dex_file != target_method.dex_file) {
+    if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
       // Calls across dex files are more likely to exceed the available BL range,
       // so use absolute patch with fixup if available and kCallArtMethod otherwise.
       HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
@@ -6759,10 +6794,13 @@
 
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
+      uint32_t offset =
+          GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
       // temp = thread->string_init_entrypoint
-      __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, invoke->GetStringInitOffset());
+      __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, offset);
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 424a1a1..6416d40 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -454,7 +454,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
   void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 7f542da..4f7f36b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -139,18 +139,18 @@
 
 // Calculate memory accessing operand for save/restore live registers.
 static void SaveRestoreLiveRegistersHelper(CodeGenerator* codegen,
-                                           RegisterSet* register_set,
+                                           LocationSummary* locations,
                                            int64_t spill_offset,
                                            bool is_save) {
-  DCHECK(ArtVixlRegCodeCoherentForRegSet(register_set->GetCoreRegisters(),
+  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
+  const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
+  DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spills,
                                          codegen->GetNumberOfCoreRegisters(),
-                                         register_set->GetFloatingPointRegisters(),
+                                         fp_spills,
                                          codegen->GetNumberOfFloatingPointRegisters()));
 
-  CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize,
-      register_set->GetCoreRegisters() & (~callee_saved_core_registers.GetList()));
-  CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize,
-      register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.GetList()));
+  CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize, core_spills);
+  CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize, fp_spills);
 
   MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
   UseScratchRegisterScope temps(masm);
@@ -184,38 +184,35 @@
 }
 
 void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  RegisterSet* register_set = locations->GetLiveRegisters();
   size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
-  for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
-    if (!codegen->IsCoreCalleeSaveRegister(i) && register_set->ContainsCoreRegister(i)) {
-      // If the register holds an object, update the stack mask.
-      if (locations->RegisterContainsObject(i)) {
-        locations->SetStackBit(stack_offset / kVRegSize);
-      }
-      DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-      DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-      saved_core_stack_offsets_[i] = stack_offset;
-      stack_offset += kXRegSizeInBytes;
+  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
+  for (uint32_t i : LowToHighBits(core_spills)) {
+    // If the register holds an object, update the stack mask.
+    if (locations->RegisterContainsObject(i)) {
+      locations->SetStackBit(stack_offset / kVRegSize);
     }
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    saved_core_stack_offsets_[i] = stack_offset;
+    stack_offset += kXRegSizeInBytes;
   }
 
-  for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) {
-    if (!codegen->IsFloatingPointCalleeSaveRegister(i) &&
-        register_set->ContainsFloatingPointRegister(i)) {
-      DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-      DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-      saved_fpu_stack_offsets_[i] = stack_offset;
-      stack_offset += kDRegSizeInBytes;
-    }
+  const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
+  for (uint32_t i : LowToHighBits(fp_spills)) {
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    saved_fpu_stack_offsets_[i] = stack_offset;
+    stack_offset += kDRegSizeInBytes;
   }
 
-  SaveRestoreLiveRegistersHelper(codegen, register_set,
+  SaveRestoreLiveRegistersHelper(codegen,
+                                 locations,
                                  codegen->GetFirstRegisterSlotInSlowPath(), true /* is_save */);
 }
 
 void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  RegisterSet* register_set = locations->GetLiveRegisters();
-  SaveRestoreLiveRegistersHelper(codegen, register_set,
+  SaveRestoreLiveRegistersHelper(codegen,
+                                 locations,
                                  codegen->GetFirstRegisterSlotInSlowPath(), false /* is_save */);
 }
 
@@ -261,10 +258,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     arm64_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -1608,7 +1601,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -2036,7 +2029,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -2059,7 +2052,8 @@
   Location index = locations->InAt(1);
   Location out = locations->Out();
   uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
-
+  const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+                                        instruction->IsStringCharAt();
   MacroAssembler* masm = GetVIXLAssembler();
   UseScratchRegisterScope temps(masm);
   // Block pools between `Load` and `MaybeRecordImplicitNullCheck`.
@@ -2077,9 +2071,28 @@
   } else {
     // General case.
     MemOperand source = HeapOperand(obj);
+    Register length;
+    if (maybe_compressed_char_at) {
+      uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+      length = temps.AcquireW();
+      __ Ldr(length, HeapOperand(obj, count_offset));
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
+    }
     if (index.IsConstant()) {
-      offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
-      source = HeapOperand(obj, offset);
+      if (maybe_compressed_char_at) {
+        vixl::aarch64::Label uncompressed_load, done;
+        __ Tbz(length.W(), kWRegSize - 1, &uncompressed_load);
+        __ Ldrb(Register(OutputCPURegister(instruction)),
+                HeapOperand(obj, offset + Int64ConstantFrom(index)));
+        __ B(&done);
+        __ Bind(&uncompressed_load);
+        __ Ldrh(Register(OutputCPURegister(instruction)),
+                HeapOperand(obj, offset + (Int64ConstantFrom(index) << 1)));
+        __ Bind(&done);
+      } else {
+        offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
+        source = HeapOperand(obj, offset);
+      }
     } else {
       Register temp = temps.AcquireSameSizeAs(obj);
       if (instruction->GetArray()->IsIntermediateAddress()) {
@@ -2097,11 +2110,24 @@
       } else {
         __ Add(temp, obj, offset);
       }
-      source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type));
+      if (maybe_compressed_char_at) {
+        vixl::aarch64::Label uncompressed_load, done;
+        __ Tbz(length.W(), kWRegSize - 1, &uncompressed_load);
+        __ Ldrb(Register(OutputCPURegister(instruction)),
+                HeapOperand(temp, XRegisterFrom(index), LSL, 0));
+        __ B(&done);
+        __ Bind(&uncompressed_load);
+        __ Ldrh(Register(OutputCPURegister(instruction)),
+                HeapOperand(temp, XRegisterFrom(index), LSL, 1));
+        __ Bind(&done);
+      } else {
+        source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type));
+      }
     }
-
-    codegen_->Load(type, OutputCPURegister(instruction), source);
-    codegen_->MaybeRecordImplicitNullCheck(instruction);
+    if (!maybe_compressed_char_at) {
+      codegen_->Load(type, OutputCPURegister(instruction), source);
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
+    }
 
     if (type == Primitive::kPrimNot) {
       static_assert(
@@ -2125,9 +2151,14 @@
 
 void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
   uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
+  vixl::aarch64::Register out = OutputRegister(instruction);
   BlockPoolsScope block_pools(GetVIXLAssembler());
-  __ Ldr(OutputRegister(instruction), HeapOperand(InputRegisterAt(instruction, 0), offset));
+  __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset));
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out compression flag from String's array length.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ And(out.W(), out.W(), Operand(static_cast<int32_t>(INT32_MAX)));
+  }
 }
 
 void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
@@ -2306,22 +2337,19 @@
 }
 
 void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1).GetCode()));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
   BoundsCheckSlowPathARM64* slow_path =
       new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(instruction);
   codegen_->AddSlowPath(slow_path);
-
   __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
   __ B(slow_path->GetEntryLabel(), hs);
 }
@@ -2685,14 +2713,8 @@
 }
 
 void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -2924,7 +2946,7 @@
 void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -3077,7 +3099,7 @@
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   if (baker_read_barrier_slow_path) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
@@ -3559,7 +3581,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method ATTRIBUTE_UNUSED) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   // On ARM64 we support all dispatch types.
   return desired_dispatch_info;
 }
@@ -3585,10 +3607,13 @@
   // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
+      uint32_t offset =
+          GetThreadOffset<kArm64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
       // temp = thread->string_init_entrypoint
-      __ Ldr(XRegisterFrom(temp), MemOperand(tr, invoke->GetStringInitOffset()));
+      __ Ldr(XRegisterFrom(temp), MemOperand(tr, offset));
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
@@ -3603,7 +3628,7 @@
       break;
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
       // Add ADRP with its PC-relative DexCache access patch.
-      const DexFile& dex_file = *invoke->GetTargetMethod().dex_file;
+      const DexFile& dex_file = invoke->GetDexFile();
       uint32_t element_offset = invoke->GetDexCacheArrayOffset();
       vixl::aarch64::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
       {
@@ -3944,7 +3969,7 @@
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
 
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
@@ -4384,7 +4409,8 @@
 }
 
 void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
-  codegen_->CreateNullCheckLocations(instruction);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
 }
 
 void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -4670,7 +4696,7 @@
 void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index f1dc7ee..a152245 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -522,7 +522,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
   void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
new file mode 100644
index 0000000..226f109
--- /dev/null
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -0,0 +1,2145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "code_generator_arm_vixl.h"
+
+#include "arch/arm/instruction_set_features_arm.h"
+#include "art_method.h"
+#include "code_generator_utils.h"
+#include "common_arm.h"
+#include "compiled_method.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "gc/accounting/card_table.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "thread.h"
+#include "utils/arm/assembler_arm_vixl.h"
+#include "utils/arm/managed_register_arm.h"
+#include "utils/assembler.h"
+#include "utils/stack_checks.h"
+
+namespace art {
+namespace arm {
+
+namespace vixl32 = vixl::aarch32;
+using namespace vixl32;  // NOLINT(build/namespaces)
+
+using helpers::DWARFReg;
+using helpers::FromLowSToD;
+using helpers::OutputRegister;
+using helpers::InputRegisterAt;
+using helpers::InputOperandAt;
+using helpers::OutputSRegister;
+using helpers::InputSRegisterAt;
+
+using RegisterList = vixl32::RegisterList;
+
+static bool ExpectedPairLayout(Location location) {
+  // We expected this for both core and fpu register pairs.
+  return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
+}
+
+static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
+
+#ifdef __
+#error "ARM Codegen VIXL macro-assembler macro already defined."
+#endif
+
+// TODO: Remove with later pop when codegen complete.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()->  // NOLINT
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
+
+// Marker that code is yet to be, and must, be implemented.
+#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+
+class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+  explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
+      : SlowPathCodeARMVIXL(instruction) {}
+
+  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARMVIXL* armvixl_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+    __ Bind(GetEntryLabel());
+    if (instruction_->CanThrowIntoCatchBlock()) {
+      // Live registers will be restored in the catch block if caught.
+      SaveLiveRegisters(codegen, instruction_->GetLocations());
+    }
+    armvixl_codegen->InvokeRuntime(kQuickThrowDivZero,
+                                   instruction_,
+                                   instruction_->GetDexPc(),
+                                   this);
+    CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
+  }
+
+  bool IsFatal() const OVERRIDE { return true; }
+
+  const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARMVIXL"; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
+};
+
+inline vixl32::Condition ARMCondition(IfCondition cond) {
+  switch (cond) {
+    case kCondEQ: return eq;
+    case kCondNE: return ne;
+    case kCondLT: return lt;
+    case kCondLE: return le;
+    case kCondGT: return gt;
+    case kCondGE: return ge;
+    case kCondB:  return lo;
+    case kCondBE: return ls;
+    case kCondA:  return hi;
+    case kCondAE: return hs;
+  }
+  LOG(FATAL) << "Unreachable";
+  UNREACHABLE();
+}
+
+// Maps signed condition to unsigned condition.
+inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
+  switch (cond) {
+    case kCondEQ: return eq;
+    case kCondNE: return ne;
+    // Signed to unsigned.
+    case kCondLT: return lo;
+    case kCondLE: return ls;
+    case kCondGT: return hi;
+    case kCondGE: return hs;
+    // Unsigned remain unchanged.
+    case kCondB:  return lo;
+    case kCondBE: return ls;
+    case kCondA:  return hi;
+    case kCondAE: return hs;
+  }
+  LOG(FATAL) << "Unreachable";
+  UNREACHABLE();
+}
+
+inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
+  // The ARM condition codes can express all the necessary branches, see the
+  // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
+  // There is no dex instruction or HIR that would need the missing conditions
+  // "equal or unordered" or "not equal".
+  switch (cond) {
+    case kCondEQ: return eq;
+    case kCondNE: return ne /* unordered */;
+    case kCondLT: return gt_bias ? cc : lt /* unordered */;
+    case kCondLE: return gt_bias ? ls : le /* unordered */;
+    case kCondGT: return gt_bias ? hi /* unordered */ : gt;
+    case kCondGE: return gt_bias ? cs /* unordered */ : ge;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
+  }
+}
+
+void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen ATTRIBUTE_UNUSED,
+                                            LocationSummary* locations ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+}
+
+void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen ATTRIBUTE_UNUSED,
+                                               LocationSummary* locations ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+}
+
+void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
+  stream << vixl32::Register(reg);
+}
+
+void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
+  stream << vixl32::SRegister(reg);
+}
+
+static uint32_t ComputeSRegisterMask(const SRegisterList& regs) {
+  uint32_t mask = 0;
+  for (uint32_t i = regs.GetFirstSRegister().GetCode();
+       i <= regs.GetLastSRegister().GetCode();
+       ++i) {
+    mask |= (1 << i);
+  }
+  return mask;
+}
+
+#undef __
+
+CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
+                                           const ArmInstructionSetFeatures& isa_features,
+                                           const CompilerOptions& compiler_options,
+                                           OptimizingCompilerStats* stats)
+    : CodeGenerator(graph,
+                    kNumberOfCoreRegisters,
+                    kNumberOfSRegisters,
+                    kNumberOfRegisterPairs,
+                    kCoreCalleeSaves.GetList(),
+                    ComputeSRegisterMask(kFpuCalleeSaves),
+                    compiler_options,
+                    stats),
+      block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      location_builder_(graph, this),
+      instruction_visitor_(graph, this),
+      move_resolver_(graph->GetArena(), this),
+      assembler_(graph->GetArena()),
+      isa_features_(isa_features) {
+  // Always save the LR register to mimic Quick.
+  AddAllocatedRegister(Location::RegisterLocation(LR));
+}
+
+#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->
+
+void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
+  GetAssembler()->FinalizeCode();
+  CodeGenerator::Finalize(allocator);
+}
+
+void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
+  // Don't allocate the dalvik style register pair passing.
+  blocked_register_pairs_[R1_R2] = true;
+
+  // Stack register, LR and PC are always reserved.
+  blocked_core_registers_[SP] = true;
+  blocked_core_registers_[LR] = true;
+  blocked_core_registers_[PC] = true;
+
+  // Reserve thread register.
+  blocked_core_registers_[TR] = true;
+
+  // Reserve temp register.
+  blocked_core_registers_[IP] = true;
+
+  if (GetGraph()->IsDebuggable()) {
+    // Stubs do not save callee-save floating point registers. If the graph
+    // is debuggable, we need to deal with these registers differently. For
+    // now, just block them.
+    for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
+         i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
+         ++i) {
+      blocked_fpu_registers_[i] = true;
+    }
+  }
+
+  UpdateBlockedPairRegisters();
+}
+
+// Blocks all register pairs containing blocked core registers.
+void CodeGeneratorARMVIXL::UpdateBlockedPairRegisters() const {
+  for (int i = 0; i < kNumberOfRegisterPairs; i++) {
+    ArmManagedRegister current =
+        ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i));
+    if (blocked_core_registers_[current.AsRegisterPairLow()]
+        || blocked_core_registers_[current.AsRegisterPairHigh()]) {
+      blocked_register_pairs_[i] = true;
+    }
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
+                                                           HBasicBlock* successor) {
+  TODO_VIXL32(FATAL);
+}
+
+InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
+                                                                 CodeGeneratorARMVIXL* codegen)
+      : InstructionCodeGenerator(graph, codegen),
+        assembler_(codegen->GetAssembler()),
+        codegen_(codegen) {}
+
+void CodeGeneratorARMVIXL::ComputeSpillMask() {
+  core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
+  DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
+  // There is no easy instruction to restore just the PC on thumb2. We spill and
+  // restore another arbitrary register.
+  core_spill_mask_ |= (1 << kCoreAlwaysSpillRegister.GetCode());
+  fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
+  // We use vpush and vpop for saving and restoring floating point registers, which take
+  // a SRegister and the number of registers to save/restore after that SRegister. We
+  // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
+  // but in the range.
+  if (fpu_spill_mask_ != 0) {
+    uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
+    uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
+    for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
+      fpu_spill_mask_ |= (1 << i);
+    }
+  }
+}
+
+void CodeGeneratorARMVIXL::GenerateFrameEntry() {
+  bool skip_overflow_check =
+      IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
+  DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+  __ Bind(&frame_entry_label_);
+
+  if (HasEmptyFrame()) {
+    return;
+  }
+
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  vixl32::Register temp = temps.Acquire();
+  if (!skip_overflow_check) {
+    __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+    // The load must immediately precede RecordPcInfo.
+    {
+      AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                 kArmInstrMaxSizeInBytes,
+                                 CodeBufferCheckScope::kMaximumSize);
+      __ ldr(temp, MemOperand(temp));
+      RecordPcInfo(nullptr, 0);
+    }
+  }
+
+  __ Push(RegisterList(core_spill_mask_));
+  GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
+  GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
+                                         0,
+                                         core_spill_mask_,
+                                         kArmWordSize);
+  if (fpu_spill_mask_ != 0) {
+    uint32_t first = LeastSignificantBit(fpu_spill_mask_);
+
+    // Check that list is contiguous.
+    DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
+
+    __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
+    GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
+    GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0),
+                                           0,
+                                           fpu_spill_mask_,
+                                           kArmWordSize);
+  }
+  int adjust = GetFrameSize() - FrameEntrySpillSize();
+  __ Sub(sp, sp, adjust);
+  GetAssembler()->cfi().AdjustCFAOffset(adjust);
+  GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
+}
+
+void CodeGeneratorARMVIXL::GenerateFrameExit() {
+  if (HasEmptyFrame()) {
+    __ Bx(lr);
+    return;
+  }
+  GetAssembler()->cfi().RememberState();
+  int adjust = GetFrameSize() - FrameEntrySpillSize();
+  __ Add(sp, sp, adjust);
+  GetAssembler()->cfi().AdjustCFAOffset(-adjust);
+  if (fpu_spill_mask_ != 0) {
+    uint32_t first = LeastSignificantBit(fpu_spill_mask_);
+
+    // Check that list is contiguous.
+    DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
+
+    __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
+    GetAssembler()->cfi().AdjustCFAOffset(
+        -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
+    GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)),
+                                      fpu_spill_mask_);
+  }
+  // Pop LR into PC to return.
+  DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
+  uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
+  __ Pop(RegisterList(pop_mask));
+  GetAssembler()->cfi().RestoreState();
+  GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
+}
+
+void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
+  __ Bind(GetLabelOf(block));
+}
+
+void CodeGeneratorARMVIXL::MoveConstant(Location destination, int32_t value) {
+  TODO_VIXL32(FATAL);
+}
+
+void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
+  TODO_VIXL32(FATAL);
+}
+
+void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
+  TODO_VIXL32(FATAL);
+}
+
+uintptr_t CodeGeneratorARMVIXL::GetAddressOf(HBasicBlock* block) {
+  TODO_VIXL32(FATAL);
+  return 0;
+}
+
+void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* null_check) {
+  TODO_VIXL32(FATAL);
+}
+
+void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* null_check) {
+  TODO_VIXL32(FATAL);
+}
+
+void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
+                                         HInstruction* instruction,
+                                         uint32_t dex_pc,
+                                         SlowPathCode* slow_path) {
+  ValidateInvokeRuntime(entrypoint, instruction, slow_path);
+  GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
+  if (EntrypointRequiresStackMap(entrypoint)) {
+    RecordPcInfo(instruction, dex_pc, slow_path);
+  }
+}
+
+void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
+                                                               HInstruction* instruction,
+                                                               SlowPathCode* slow_path) {
+  ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
+  GenerateInvokeRuntime(entry_point_offset);
+}
+
+void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
+  GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
+  __ Blx(lr);
+}
+
+// Check if the desired_string_load_kind is supported. If it is, return it,
+// otherwise return a fall-back kind that should be used instead.
+HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
+      HLoadString::LoadKind desired_string_load_kind) {
+  TODO_VIXL32(FATAL);
+  return desired_string_load_kind;
+}
+
+// Check if the desired_class_load_kind is supported. If it is, return it,
+// otherwise return a fall-back kind that should be used instead.
+HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
+      HLoadClass::LoadKind desired_class_load_kind) {
+  TODO_VIXL32(FATAL);
+  return desired_class_load_kind;
+}
+
+// Check if the desired_dispatch_info is supported. If it is, return it,
+// otherwise return a fall-back info that should be used instead.
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
+      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+      HInvokeStaticOrDirect* invoke) {
+  TODO_VIXL32(FATAL);
+  return desired_dispatch_info;
+}
+
+// Generate a call to a static or direct method.
+void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+                                                      Location temp) {
+  TODO_VIXL32(FATAL);
+}
+
+// Generate a call to a virtual method.
+void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) {
+  TODO_VIXL32(FATAL);
+}
+
+// Copy the result of a call into the given target.
+void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
+  TODO_VIXL32(FATAL);
+}
+
+void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+  DCHECK(!successor->IsExitBlock());
+  HBasicBlock* block = got->GetBlock();
+  HInstruction* previous = got->GetPrevious();
+  HLoopInformation* info = block->GetLoopInformation();
+
+  if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+    GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+    return;
+  }
+  if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+    GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+  }
+  if (!codegen_->GoesToNextBlock(block, successor)) {
+    __ B(codegen_->GetLabelOf(successor));
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
+  got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
+  HandleGoto(got, got->GetSuccessor());
+}
+
+void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
+  exit->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateVcmp(HInstruction* instruction) {
+  Primitive::Type type = instruction->InputAt(0)->GetType();
+  Location lhs_loc = instruction->GetLocations()->InAt(0);
+  Location rhs_loc = instruction->GetLocations()->InAt(1);
+  if (rhs_loc.IsConstant()) {
+    // 0.0 is the only immediate that can be encoded directly in
+    // a VCMP instruction.
+    //
+    // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
+    // specify that in a floating-point comparison, positive zero
+    // and negative zero are considered equal, so we can use the
+    // literal 0.0 for both cases here.
+    //
+    // Note however that some methods (Float.equal, Float.compare,
+    // Float.compareTo, Double.equal, Double.compare,
+    // Double.compareTo, Math.max, Math.min, StrictMath.max,
+    // StrictMath.min) consider 0.0 to be (strictly) greater than
+    // -0.0. So if we ever translate calls to these methods into a
+    // HCompare instruction, we must handle the -0.0 case with
+    // care here.
+    DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
+    if (type == Primitive::kPrimFloat) {
+      __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
+    } else {
+      DCHECK_EQ(type, Primitive::kPrimDouble);
+      __ Vcmp(F64, FromLowSToD(lhs_loc.AsFpuRegisterPairLow<vixl32::SRegister>()), 0.0);
+    }
+  } else {
+    if (type == Primitive::kPrimFloat) {
+      __ Vcmp(F32, InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
+    } else {
+      DCHECK_EQ(type, Primitive::kPrimDouble);
+      __ Vcmp(F64,
+              FromLowSToD(lhs_loc.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(rhs_loc.AsFpuRegisterPairLow<vixl32::SRegister>()));
+    }
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond,
+                                                      vixl32::Label* true_label,
+                                                      vixl32::Label* false_label ATTRIBUTE_UNUSED) {
+  // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS).
+  __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+  __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label);
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond,
+                                                                   vixl32::Label* true_label,
+                                                                   vixl32::Label* false_label) {
+  LocationSummary* locations = cond->GetLocations();
+  Location left = locations->InAt(0);
+  Location right = locations->InAt(1);
+  IfCondition if_cond = cond->GetCondition();
+
+  vixl32::Register left_high = left.AsRegisterPairHigh<vixl32::Register>();
+  vixl32::Register left_low = left.AsRegisterPairLow<vixl32::Register>();
+  IfCondition true_high_cond = if_cond;
+  IfCondition false_high_cond = cond->GetOppositeCondition();
+  vixl32::Condition final_condition = ARMUnsignedCondition(if_cond);  // unsigned on lower part
+
+  // Set the conditions for the test, remembering that == needs to be
+  // decided using the low words.
+  // TODO: consider avoiding jumps with temporary and CMP low+SBC high
+  switch (if_cond) {
+    case kCondEQ:
+    case kCondNE:
+      // Nothing to do.
+      break;
+    case kCondLT:
+      false_high_cond = kCondGT;
+      break;
+    case kCondLE:
+      true_high_cond = kCondLT;
+      break;
+    case kCondGT:
+      false_high_cond = kCondLT;
+      break;
+    case kCondGE:
+      true_high_cond = kCondGT;
+      break;
+    case kCondB:
+      false_high_cond = kCondA;
+      break;
+    case kCondBE:
+      true_high_cond = kCondB;
+      break;
+    case kCondA:
+      false_high_cond = kCondB;
+      break;
+    case kCondAE:
+      true_high_cond = kCondA;
+      break;
+  }
+  if (right.IsConstant()) {
+    int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+    int32_t val_low = Low32Bits(value);
+    int32_t val_high = High32Bits(value);
+
+    __ Cmp(left_high, val_high);
+    if (if_cond == kCondNE) {
+      __ B(ARMCondition(true_high_cond), true_label);
+    } else if (if_cond == kCondEQ) {
+      __ B(ARMCondition(false_high_cond), false_label);
+    } else {
+      __ B(ARMCondition(true_high_cond), true_label);
+      __ B(ARMCondition(false_high_cond), false_label);
+    }
+    // Must be equal high, so compare the lows.
+    __ Cmp(left_low, val_low);
+  } else {
+    vixl32::Register right_high = right.AsRegisterPairHigh<vixl32::Register>();
+    vixl32::Register right_low = right.AsRegisterPairLow<vixl32::Register>();
+
+    __ Cmp(left_high, right_high);
+    if (if_cond == kCondNE) {
+      __ B(ARMCondition(true_high_cond), true_label);
+    } else if (if_cond == kCondEQ) {
+      __ B(ARMCondition(false_high_cond), false_label);
+    } else {
+      __ B(ARMCondition(true_high_cond), true_label);
+      __ B(ARMCondition(false_high_cond), false_label);
+    }
+    // Must be equal high, so compare the lows.
+    __ Cmp(left_low, right_low);
+  }
+  // The last comparison might be unsigned.
+  // TODO: optimize cases where this is always true/false
+  __ B(final_condition, true_label);
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
+                                                                   vixl32::Label* true_target_in,
+                                                                   vixl32::Label* false_target_in) {
+  // Generated branching requires both targets to be explicit. If either of the
+  // targets is nullptr (fallthrough) use and bind `fallthrough` instead.
+  vixl32::Label fallthrough;
+  vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in;
+  vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in;
+
+  Primitive::Type type = condition->InputAt(0)->GetType();
+  switch (type) {
+    case Primitive::kPrimLong:
+      GenerateLongComparesAndJumps(condition, true_target, false_target);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      GenerateVcmp(condition);
+      GenerateFPJumps(condition, true_target, false_target);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected compare type " << type;
+  }
+
+  if (false_target != &fallthrough) {
+    __ B(false_target);
+  }
+
+  if (true_target_in == nullptr || false_target_in == nullptr) {
+    __ Bind(&fallthrough);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
+                                                            size_t condition_input_index,
+                                                            vixl32::Label* true_target,
+                                                            vixl32::Label* false_target) {
+  HInstruction* cond = instruction->InputAt(condition_input_index);
+
+  if (true_target == nullptr && false_target == nullptr) {
+    // Nothing to do. The code always falls through.
+    return;
+  } else if (cond->IsIntConstant()) {
+    // Constant condition, statically compared against "true" (integer value 1).
+    if (cond->AsIntConstant()->IsTrue()) {
+      if (true_target != nullptr) {
+        __ B(true_target);
+      }
+    } else {
+      DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
+      if (false_target != nullptr) {
+        __ B(false_target);
+      }
+    }
+    return;
+  }
+
+  // The following code generates these patterns:
+  //  (1) true_target == nullptr && false_target != nullptr
+  //        - opposite condition true => branch to false_target
+  //  (2) true_target != nullptr && false_target == nullptr
+  //        - condition true => branch to true_target
+  //  (3) true_target != nullptr && false_target != nullptr
+  //        - condition true => branch to true_target
+  //        - branch to false_target
+  if (IsBooleanValueOrMaterializedCondition(cond)) {
+    // Condition has been materialized, compare the output to 0.
+    if (kIsDebugBuild) {
+      Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
+      DCHECK(cond_val.IsRegister());
+    }
+    if (true_target == nullptr) {
+      __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
+    } else {
+      __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
+    }
+  } else {
+    // Condition has not been materialized. Use its inputs as the comparison and
+    // its condition as the branch condition.
+    HCondition* condition = cond->AsCondition();
+
+    // If this is a long or FP comparison that has been folded into
+    // the HCondition, generate the comparison directly.
+    Primitive::Type type = condition->InputAt(0)->GetType();
+    if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
+      GenerateCompareTestAndBranch(condition, true_target, false_target);
+      return;
+    }
+
+    LocationSummary* locations = cond->GetLocations();
+    DCHECK(locations->InAt(0).IsRegister());
+    vixl32::Register left = InputRegisterAt(cond, 0);
+    Location right = locations->InAt(1);
+    if (right.IsRegister()) {
+      __ Cmp(left, InputRegisterAt(cond, 1));
+    } else {
+      DCHECK(right.IsConstant());
+      __ Cmp(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
+    }
+    if (true_target == nullptr) {
+      __ B(ARMCondition(condition->GetOppositeCondition()), false_target);
+    } else {
+      __ B(ARMCondition(condition->GetCondition()), true_target);
+    }
+  }
+
+  // If neither branch falls through (case 3), the conditional branch to `true_target`
+  // was already emitted (case 2) and we need to emit a jump to `false_target`.
+  if (true_target != nullptr && false_target != nullptr) {
+    __ B(false_target);
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
+    locations->SetInAt(0, Location::RequiresRegister());
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
+  HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
+  HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
+  vixl32::Label* true_target =
+      codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
+          nullptr : codegen_->GetLabelOf(true_successor);
+  vixl32::Label* false_target =
+      codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
+          nullptr : codegen_->GetLabelOf(false_successor);
+  GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
+}
+
+void CodeGeneratorARMVIXL::GenerateNop() {
+  __ Nop();
+}
+
+void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
+  // Handle the long/FP comparisons made in instruction simplification.
+  switch (cond->InputAt(0)->GetType()) {
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+      if (!cond->IsEmittedAtUseSite()) {
+        locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+      }
+      break;
+
+    // TODO: https://android-review.googlesource.com/#/c/252265/
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      if (!cond->IsEmittedAtUseSite()) {
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      }
+      break;
+
+    default:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+      if (!cond->IsEmittedAtUseSite()) {
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      }
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
+  if (cond->IsEmittedAtUseSite()) {
+    return;
+  }
+
+  LocationSummary* locations = cond->GetLocations();
+  Location right = locations->InAt(1);
+  vixl32::Register out = OutputRegister(cond);
+  vixl32::Label true_label, false_label;
+
+  switch (cond->InputAt(0)->GetType()) {
+    default: {
+      // Integer case.
+      if (right.IsRegister()) {
+        __ Cmp(InputRegisterAt(cond, 0), InputRegisterAt(cond, 1));
+      } else {
+        DCHECK(right.IsConstant());
+        __ Cmp(InputRegisterAt(cond, 0), CodeGenerator::GetInt32ValueOf(right.GetConstant()));
+      }
+      {
+        AssemblerAccurateScope aas(GetVIXLAssembler(),
+                                   kArmInstrMaxSizeInBytes * 3u,
+                                   CodeBufferCheckScope::kMaximumSize);
+        __ ite(ARMCondition(cond->GetCondition()));
+        __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
+        __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0);
+      }
+      return;
+    }
+    case Primitive::kPrimLong:
+      GenerateLongComparesAndJumps(cond, &true_label, &false_label);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      GenerateVcmp(cond);
+      GenerateFPJumps(cond, &true_label, &false_label);
+      break;
+  }
+
+  // Convert the jumps into the result.
+  vixl32::Label done_label;
+
+  // False case: result = 0.
+  __ Bind(&false_label);
+  __ Mov(out, 0);
+  __ B(&done_label);
+
+  // True case: result = 1.
+  __ Bind(&true_label);
+  __ Mov(out, 1);
+  __ Bind(&done_label);
+}
+
+void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
+  HandleCondition(comp);
+}
+
+void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
+  // Will be generated at use site.
+}
+
+void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+  locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
+  // Will be generated at use site.
+}
+
+void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  memory_barrier->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
+  codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+}
+
+void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
+  ret->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
+  codegen_->GenerateFrameExit();
+}
+
+void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
+  locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret ATTRIBUTE_UNUSED) {
+  codegen_->GenerateFrameExit();
+}
+
+void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  DCHECK_NE(result_type, input_type);
+
+  // The float-to-long, double-to-long and long-to-float type conversions
+  // rely on a call to the runtime.
+  LocationSummary::CallKind call_kind =
+      (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+        && result_type == Primitive::kPrimLong)
+       || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
+      ? LocationSummary::kCallOnMainOnly
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+
+  // The Java language does not allow treating boolean as an integral type but
+  // our bit representation makes it safe.
+
+  switch (result_type) {
+    case Primitive::kPrimByte:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to byte is a result of code transformations.
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-byte' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimShort:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to short is a result of code transformations.
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-short' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Processing a Dex `long-to-int' instruction.
+          locations->SetInAt(0, Location::Any());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-int' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
+          break;
+
+        case Primitive::kPrimDouble:
+          // Processing a Dex `double-to-int' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-long' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        case Primitive::kPrimFloat: {
+          // Processing a Dex `float-to-long' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          locations->SetInAt(0, Location::FpuRegisterLocation(
+              calling_convention.GetFpuRegisterAt(0)));
+          locations->SetOut(Location::RegisterPairLocation(R0, R1));
+          break;
+        }
+
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-long' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          locations->SetInAt(0, Location::FpuRegisterPairLocation(
+              calling_convention.GetFpuRegisterAt(0),
+              calling_convention.GetFpuRegisterAt(1)));
+          locations->SetOut(Location::RegisterPairLocation(R0, R1));
+          break;
+        }
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimChar:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to char is a result of code transformations.
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+          // Processing a Dex `int-to-char' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimFloat:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-float' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresFpuRegister());
+          break;
+
+        case Primitive::kPrimLong: {
+          // Processing a Dex `long-to-float' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          locations->SetInAt(0, Location::RegisterPairLocation(
+              calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
+          locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+          break;
+        }
+
+        case Primitive::kPrimDouble:
+          // Processing a Dex `double-to-float' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      };
+      break;
+
+    case Primitive::kPrimDouble:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresFpuRegister());
+          break;
+
+        case Primitive::kPrimLong:
+          // Processing a Dex `long-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresRegister());
+          locations->SetOut(Location::RequiresFpuRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
+          break;
+
+        case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      };
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
+  LocationSummary* locations = conversion->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  Primitive::Type result_type = conversion->GetResultType();
+  Primitive::Type input_type = conversion->GetInputType();
+  DCHECK_NE(result_type, input_type);
+  switch (result_type) {
+    case Primitive::kPrimByte:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to byte is a result of code transformations.
+          __ Sbfx(OutputRegister(conversion), in.AsRegisterPairLow<vixl32::Register>(), 0, 8);
+          break;
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-byte' instruction.
+          __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimShort:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to short is a result of code transformations.
+          __ Sbfx(OutputRegister(conversion), in.AsRegisterPairLow<vixl32::Register>(), 0, 16);
+          break;
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-short' instruction.
+          __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimInt:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Processing a Dex `long-to-int' instruction.
+          DCHECK(out.IsRegister());
+          if (in.IsRegisterPair()) {
+            __ Mov(OutputRegister(conversion), in.AsRegisterPairLow<vixl32::Register>());
+          } else if (in.IsDoubleStackSlot()) {
+            GetAssembler()->LoadFromOffset(kLoadWord,
+                                           OutputRegister(conversion),
+                                           sp,
+                                           in.GetStackIndex());
+          } else {
+            DCHECK(in.IsConstant());
+            DCHECK(in.GetConstant()->IsLongConstant());
+            int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+            __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
+          }
+          break;
+
+        case Primitive::kPrimFloat: {
+          // Processing a Dex `float-to-int' instruction.
+          vixl32::SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<vixl32::SRegister>();
+          __ Vcvt(I32, F32, temp, InputSRegisterAt(conversion, 0));
+          __ Vmov(OutputRegister(conversion), temp);
+          break;
+        }
+
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-int' instruction.
+          vixl32::SRegister temp_s =
+              locations->GetTemp(0).AsFpuRegisterPairLow<vixl32::SRegister>();
+          __ Vcvt(I32, F64, temp_s, FromLowSToD(in.AsFpuRegisterPairLow<vixl32::SRegister>()));
+          __ Vmov(OutputRegister(conversion), temp_s);
+          break;
+        }
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimLong:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar:
+          // Processing a Dex `int-to-long' instruction.
+          DCHECK(out.IsRegisterPair());
+          DCHECK(in.IsRegister());
+          __ Mov(out.AsRegisterPairLow<vixl32::Register>(), InputRegisterAt(conversion, 0));
+          // Sign extension.
+          __ Asr(out.AsRegisterPairHigh<vixl32::Register>(),
+                 out.AsRegisterPairLow<vixl32::Register>(),
+                 31);
+          break;
+
+        case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-long' instruction.
+          codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
+          CheckEntrypointTypes<kQuickF2l, int64_t, float>();
+          break;
+
+        case Primitive::kPrimDouble:
+          // Processing a Dex `double-to-long' instruction.
+          codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
+          CheckEntrypointTypes<kQuickD2l, int64_t, double>();
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimChar:
+      switch (input_type) {
+        case Primitive::kPrimLong:
+          // Type conversion from long to char is a result of code transformations.
+          __ Ubfx(OutputRegister(conversion), in.AsRegisterPairLow<vixl32::Register>(), 0, 16);
+          break;
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+          // Processing a Dex `int-to-char' instruction.
+          __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      }
+      break;
+
+    case Primitive::kPrimFloat:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar: {
+          // Processing a Dex `int-to-float' instruction.
+          __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
+          __ Vcvt(F32, I32, OutputSRegister(conversion), OutputSRegister(conversion));
+          break;
+        }
+
+        case Primitive::kPrimLong:
+          // Processing a Dex `long-to-float' instruction.
+          codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
+          CheckEntrypointTypes<kQuickL2f, float, int64_t>();
+          break;
+
+        case Primitive::kPrimDouble:
+          // Processing a Dex `double-to-float' instruction.
+          __ Vcvt(F32,
+                  F64,
+                  OutputSRegister(conversion),
+                  FromLowSToD(in.AsFpuRegisterPairLow<vixl32::SRegister>()));
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      };
+      break;
+
+    case Primitive::kPrimDouble:
+      switch (input_type) {
+        case Primitive::kPrimBoolean:
+          // Boolean input is a result of code transformations.
+        case Primitive::kPrimByte:
+        case Primitive::kPrimShort:
+        case Primitive::kPrimInt:
+        case Primitive::kPrimChar: {
+          // Processing a Dex `int-to-double' instruction.
+          __ Vmov(out.AsFpuRegisterPairLow<vixl32::SRegister>(), InputRegisterAt(conversion, 0));
+          __ Vcvt(F64,
+                  I32,
+                  FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+                  out.AsFpuRegisterPairLow<vixl32::SRegister>());
+          break;
+        }
+
+        case Primitive::kPrimLong: {
+          // Processing a Dex `long-to-double' instruction.
+          vixl32::Register low = in.AsRegisterPairLow<vixl32::Register>();
+          vixl32::Register high = in.AsRegisterPairHigh<vixl32::Register>();
+
+          vixl32::SRegister out_s = out.AsFpuRegisterPairLow<vixl32::SRegister>();
+          vixl32::DRegister out_d = FromLowSToD(out_s);
+
+          vixl32::SRegister temp_s =
+              locations->GetTemp(0).AsFpuRegisterPairLow<vixl32::SRegister>();
+          vixl32::DRegister temp_d = FromLowSToD(temp_s);
+
+          vixl32::SRegister constant_s =
+              locations->GetTemp(1).AsFpuRegisterPairLow<vixl32::SRegister>();
+          vixl32::DRegister constant_d = FromLowSToD(constant_s);
+
+          // temp_d = int-to-double(high)
+          __ Vmov(temp_s, high);
+          __ Vcvt(F64, I32, temp_d, temp_s);
+          // constant_d = k2Pow32EncodingForDouble
+          __ Vmov(F64,
+                  constant_d,
+                  vixl32::DOperand(bit_cast<double, int64_t>(k2Pow32EncodingForDouble)));
+          // out_d = unsigned-to-double(low)
+          __ Vmov(out_s, low);
+          __ Vcvt(F64, U32, out_d, out_s);
+          // out_d += temp_d * constant_d
+          __ Vmla(F64, out_d, temp_d, constant_d);
+          break;
+        }
+
+        case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-double' instruction.
+          __ Vcvt(F64,
+                  F32,
+                  FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+                  InputSRegisterAt(conversion, 0));
+          break;
+
+        default:
+          LOG(FATAL) << "Unexpected type conversion from " << input_type
+                     << " to " << result_type;
+      };
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected type conversion from " << input_type
+                 << " to " << result_type;
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
+  switch (add->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    // TODO: https://android-review.googlesource.com/#/c/254144/
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
+  LocationSummary* locations = add->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+
+  switch (add->GetResultType()) {
+    case Primitive::kPrimInt: {
+      __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
+      }
+      break;
+
+    // TODO: https://android-review.googlesource.com/#/c/254144/
+    case Primitive::kPrimLong: {
+      DCHECK(second.IsRegisterPair());
+      __ Adds(out.AsRegisterPairLow<vixl32::Register>(),
+              first.AsRegisterPairLow<vixl32::Register>(),
+              Operand(second.AsRegisterPairLow<vixl32::Register>()));
+      __ Adc(out.AsRegisterPairHigh<vixl32::Register>(),
+             first.AsRegisterPairHigh<vixl32::Register>(),
+             second.AsRegisterPairHigh<vixl32::Register>());
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ Vadd(F32, OutputSRegister(add), InputSRegisterAt(add, 0), InputSRegisterAt(add, 1));
+      }
+      break;
+
+    case Primitive::kPrimDouble:
+      __ Vadd(F64,
+              FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(first.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(second.AsFpuRegisterPairLow<vixl32::SRegister>()));
+      break;
+
+    default:
+      LOG(FATAL) << "Unexpected add type " << add->GetResultType();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    // TODO: https://android-review.googlesource.com/#/c/254144/
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
+  LocationSummary* locations = sub->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt: {
+      if (second.IsRegister()) {
+        __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputRegisterAt(sub, 1));
+      } else {
+        __ Sub(OutputRegister(sub),
+               InputRegisterAt(sub, 0),
+               second.GetConstant()->AsIntConstant()->GetValue());
+      }
+      break;
+    }
+
+    // TODO: https://android-review.googlesource.com/#/c/254144/
+    case Primitive::kPrimLong: {
+      DCHECK(second.IsRegisterPair());
+      __ Subs(out.AsRegisterPairLow<vixl32::Register>(),
+              first.AsRegisterPairLow<vixl32::Register>(),
+              Operand(second.AsRegisterPairLow<vixl32::Register>()));
+      __ Sbc(out.AsRegisterPairHigh<vixl32::Register>(),
+             first.AsRegisterPairHigh<vixl32::Register>(),
+             Operand(second.AsRegisterPairHigh<vixl32::Register>()));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ Vsub(F32, OutputSRegister(sub), InputSRegisterAt(sub, 0), InputSRegisterAt(sub, 1));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ Vsub(F64,
+              FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(first.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(second.AsFpuRegisterPairLow<vixl32::SRegister>()));
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:  {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
+  LocationSummary* locations = mul->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  switch (mul->GetResultType()) {
+    case Primitive::kPrimInt: {
+      __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
+      break;
+    }
+    case Primitive::kPrimLong: {
+      vixl32::Register out_hi = out.AsRegisterPairHigh<vixl32::Register>();
+      vixl32::Register out_lo = out.AsRegisterPairLow<vixl32::Register>();
+      vixl32::Register in1_hi = first.AsRegisterPairHigh<vixl32::Register>();
+      vixl32::Register in1_lo = first.AsRegisterPairLow<vixl32::Register>();
+      vixl32::Register in2_hi = second.AsRegisterPairHigh<vixl32::Register>();
+      vixl32::Register in2_lo = second.AsRegisterPairLow<vixl32::Register>();
+
+      // Extra checks to protect caused by the existence of R1_R2.
+      // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
+      // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
+      DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
+      DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
+
+      // input: in1 - 64 bits, in2 - 64 bits
+      // output: out
+      // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
+      // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
+      // parts: out.lo = (in1.lo * in2.lo)[31:0]
+
+      UseScratchRegisterScope temps(GetVIXLAssembler());
+      vixl32::Register temp = temps.Acquire();
+      // temp <- in1.lo * in2.hi
+      __ Mul(temp, in1_lo, in2_hi);
+      // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
+      __ Mla(out_hi, in1_hi, in2_lo, temp);
+      // out.lo <- (in1.lo * in2.lo)[31:0];
+      __ Umull(out_lo, temp, in1_lo, in2_lo);
+      // out.hi <- in2.hi * in1.lo +  in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
+      __ Add(out_hi, out_hi, Operand(temp));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ Vmul(F32, OutputSRegister(mul), InputSRegisterAt(mul, 0), InputSRegisterAt(mul, 1));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ Vmul(F64,
+              FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(first.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(second.AsFpuRegisterPairLow<vixl32::SRegister>()));
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
+  LocationSummary* locations = not_->GetLocations();
+  Location out = locations->Out();
+  Location in = locations->InAt(0);
+  switch (not_->GetResultType()) {
+    case Primitive::kPrimInt:
+      __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
+      break;
+
+    case Primitive::kPrimLong:
+      __ Mvn(out.AsRegisterPairLow<vixl32::Register>(),
+             Operand(in.AsRegisterPairLow<vixl32::Register>()));
+      __ Mvn(out.AsRegisterPairHigh<vixl32::Register>(),
+             Operand(in.AsRegisterPairHigh<vixl32::Register>()));
+      break;
+
+    default:
+      LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
+  }
+}
+
+void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
+  // TODO (ported from quick): revisit ARM barrier kinds.
+  DmbOptions flavor = DmbOptions::ISH;  // Quiet C++ warnings.
+  switch (kind) {
+    case MemBarrierKind::kAnyStore:
+    case MemBarrierKind::kLoadAny:
+    case MemBarrierKind::kAnyAny: {
+      flavor = DmbOptions::ISH;
+      break;
+    }
+    case MemBarrierKind::kStoreStore: {
+      flavor = DmbOptions::ISHST;
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected memory barrier " << kind;
+  }
+  __ Dmb(flavor);
+}
+
+void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  vixl32::Register out = OutputRegister(instruction);
+  vixl32::Register dividend = InputRegisterAt(instruction, 0);
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  DCHECK(imm == 1 || imm == -1);
+
+  if (instruction->IsRem()) {
+    __ Mov(out, 0);
+  } else {
+    if (imm == 1) {
+      __ Mov(out, dividend);
+    } else {
+      __ Rsb(out, dividend, 0);
+    }
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  vixl32::Register out = OutputRegister(instruction);
+  vixl32::Register dividend = InputRegisterAt(instruction, 0);
+  vixl32::Register temp = locations->GetTemp(0).AsRegister<vixl32::Register>();
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
+  int ctz_imm = CTZ(abs_imm);
+
+  if (ctz_imm == 1) {
+    __ Lsr(temp, dividend, 32 - ctz_imm);
+  } else {
+    __ Asr(temp, dividend, 31);
+    __ Lsr(temp, temp, 32 - ctz_imm);
+  }
+  __ Add(out, temp, Operand(dividend));
+
+  if (instruction->IsDiv()) {
+    __ Asr(out, out, ctz_imm);
+    if (imm < 0) {
+      __ Rsb(out, out, Operand(0));
+    }
+  } else {
+    __ Ubfx(out, out, 0, ctz_imm);
+    __ Sub(out, out, Operand(temp));
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  vixl32::Register out = OutputRegister(instruction);
+  vixl32::Register dividend = InputRegisterAt(instruction, 0);
+  vixl32::Register temp1 = locations->GetTemp(0).AsRegister<vixl32::Register>();
+  vixl32::Register temp2 = locations->GetTemp(1).AsRegister<vixl32::Register>();
+  int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+  int64_t magic;
+  int shift;
+  CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+  __ Mov(temp1, magic);
+  __ Smull(temp2, temp1, dividend, temp1);
+
+  if (imm > 0 && magic < 0) {
+    __ Add(temp1, temp1, Operand(dividend));
+  } else if (imm < 0 && magic > 0) {
+    __ Sub(temp1, temp1, Operand(dividend));
+  }
+
+  if (shift != 0) {
+    __ Asr(temp1, temp1, shift);
+  }
+
+  if (instruction->IsDiv()) {
+    __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
+  } else {
+    __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
+    // TODO: Strength reduction for mls.
+    __ Mov(temp2, imm);
+    __ Mls(out, temp1, temp2, dividend);
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
+    HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  if (imm == 0) {
+    // Do not generate anything. DivZeroCheck would prevent any code to be executed.
+  } else if (imm == 1 || imm == -1) {
+    DivRemOneOrMinusOne(instruction);
+  } else if (IsPowerOfTwo(AbsOrMin(imm))) {
+    DivRemByPowerOfTwo(instruction);
+  } else {
+    DCHECK(imm <= -2 || imm >= 2);
+    GenerateDivRemWithAnyConstant(instruction);
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
+  LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
+  if (div->GetResultType() == Primitive::kPrimLong) {
+    // pLdiv runtime call.
+    call_kind = LocationSummary::kCallOnMainOnly;
+  } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
+    // sdiv will be replaced by other instruction sequence.
+  } else if (div->GetResultType() == Primitive::kPrimInt &&
+             !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+    // pIdivmod runtime call.
+    call_kind = LocationSummary::kCallOnMainOnly;
+  }
+
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind);
+
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      if (div->InputAt(1)->IsConstant()) {
+        locations->SetInAt(0, Location::RequiresRegister());
+        locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+        int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
+        if (value == 1 || value == 0 || value == -1) {
+          // No temp register required.
+        } else {
+          locations->AddTemp(Location::RequiresRegister());
+          if (!IsPowerOfTwo(AbsOrMin(value))) {
+            locations->AddTemp(Location::RequiresRegister());
+          }
+        }
+      } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+        locations->SetInAt(0, Location::RequiresRegister());
+        locations->SetInAt(1, Location::RequiresRegister());
+        locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      } else {
+        TODO_VIXL32(FATAL);
+      }
+      break;
+    }
+    case Primitive::kPrimLong: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
+  LocationSummary* locations = div->GetLocations();
+  Location out = locations->Out();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+
+  switch (div->GetResultType()) {
+    case Primitive::kPrimInt: {
+      if (second.IsConstant()) {
+        GenerateDivRemConstantIntegral(div);
+      } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+        __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
+      } else {
+        TODO_VIXL32(FATAL);
+      }
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      TODO_VIXL32(FATAL);
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ Vdiv(F32, OutputSRegister(div), InputSRegisterAt(div, 0), InputSRegisterAt(div, 1));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ Vdiv(F64,
+              FromLowSToD(out.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(first.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              FromLowSToD(second.AsFpuRegisterPairLow<vixl32::SRegister>()));
+      break;
+    }
+
+    default:
+      LOG(FATAL) << "Unexpected div type " << div->GetResultType();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
+  if (instruction->HasUses()) {
+    locations->SetOut(Location::SameAsFirstInput());
+  }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+  DivZeroCheckSlowPathARMVIXL* slow_path =
+      new (GetGraph()->GetArena()) DivZeroCheckSlowPathARMVIXL(instruction);
+  codegen_->AddSlowPath(slow_path);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location value = locations->InAt(0);
+
+  switch (instruction->GetType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt: {
+      if (value.IsRegister()) {
+        __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+      } else {
+        DCHECK(value.IsConstant()) << value;
+        if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+          __ B(slow_path->GetEntryLabel());
+        }
+      }
+      break;
+    }
+    case Primitive::kPrimLong: {
+      if (value.IsRegisterPair()) {
+        UseScratchRegisterScope temps(GetVIXLAssembler());
+        vixl32::Register temp = temps.Acquire();
+        __ Orrs(temp,
+                value.AsRegisterPairLow<vixl32::Register>(),
+                Operand(value.AsRegisterPairHigh<vixl32::Register>()));
+        __ B(eq, slow_path->GetEntryLabel());
+      } else {
+        DCHECK(value.IsConstant()) << value;
+        if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
+          __ B(slow_path->GetEntryLabel());
+        }
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "Unreachable";
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
+  codegen_->GetMoveResolver()->EmitNativeCode(instruction);
+}
+
+ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
+  return codegen_->GetAssembler();
+}
+
+void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
+  MoveOperands* move = moves_[index];
+  Location source = move->GetSource();
+  Location destination = move->GetDestination();
+
+  if (source.IsRegister()) {
+    if (destination.IsRegister()) {
+      __ Mov(destination.AsRegister<vixl32::Register>(), source.AsRegister<vixl32::Register>());
+    } else if (destination.IsFpuRegister()) {
+      __ Vmov(destination.AsFpuRegister<vixl32::SRegister>(),
+              source.AsRegister<vixl32::Register>());
+    } else {
+      DCHECK(destination.IsStackSlot());
+      GetAssembler()->StoreToOffset(kStoreWord,
+                                    source.AsRegister<vixl32::Register>(),
+                                    sp,
+                                    destination.GetStackIndex());
+    }
+  } else if (source.IsStackSlot()) {
+    TODO_VIXL32(FATAL);
+  } else if (source.IsFpuRegister()) {
+    TODO_VIXL32(FATAL);
+  } else if (source.IsDoubleStackSlot()) {
+    TODO_VIXL32(FATAL);
+  } else if (source.IsRegisterPair()) {
+    if (destination.IsRegisterPair()) {
+      __ Mov(destination.AsRegisterPairLow<vixl32::Register>(),
+             source.AsRegisterPairLow<vixl32::Register>());
+      __ Mov(destination.AsRegisterPairHigh<vixl32::Register>(),
+             source.AsRegisterPairHigh<vixl32::Register>());
+    } else if (destination.IsFpuRegisterPair()) {
+      __ Vmov(FromLowSToD(destination.AsFpuRegisterPairLow<vixl32::SRegister>()),
+              source.AsRegisterPairLow<vixl32::Register>(),
+              source.AsRegisterPairHigh<vixl32::Register>());
+    } else {
+      DCHECK(destination.IsDoubleStackSlot()) << destination;
+      DCHECK(ExpectedPairLayout(source));
+      GetAssembler()->StoreToOffset(kStoreWordPair,
+                                    source.AsRegisterPairLow<vixl32::Register>(),
+                                    sp,
+                                    destination.GetStackIndex());
+    }
+  } else if (source.IsFpuRegisterPair()) {
+    TODO_VIXL32(FATAL);
+  } else {
+    DCHECK(source.IsConstant()) << source;
+    HConstant* constant = source.GetConstant();
+    if (constant->IsIntConstant() || constant->IsNullConstant()) {
+      int32_t value = CodeGenerator::GetInt32ValueOf(constant);
+      if (destination.IsRegister()) {
+        __ Mov(destination.AsRegister<vixl32::Register>(), value);
+      } else {
+        DCHECK(destination.IsStackSlot());
+        UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+        vixl32::Register temp = temps.Acquire();
+        __ Mov(temp, value);
+        GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
+      }
+    } else if (constant->IsLongConstant()) {
+      int64_t value = constant->AsLongConstant()->GetValue();
+      if (destination.IsRegisterPair()) {
+        __ Mov(destination.AsRegisterPairLow<vixl32::Register>(), Low32Bits(value));
+        __ Mov(destination.AsRegisterPairHigh<vixl32::Register>(), High32Bits(value));
+      } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
+        UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+        vixl32::Register temp = temps.Acquire();
+        __ Mov(temp, Low32Bits(value));
+        GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
+        __ Mov(temp, High32Bits(value));
+        GetAssembler()->StoreToOffset(kStoreWord,
+                                      temp,
+                                      sp,
+                                      destination.GetHighStackIndex(kArmWordSize));
+      }
+    } else if (constant->IsDoubleConstant()) {
+      double value = constant->AsDoubleConstant()->GetValue();
+      if (destination.IsFpuRegisterPair()) {
+        __ Vmov(F64, FromLowSToD(destination.AsFpuRegisterPairLow<vixl32::SRegister>()), value);
+      } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
+        uint64_t int_value = bit_cast<uint64_t, double>(value);
+        UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+        vixl32::Register temp = temps.Acquire();
+        GetAssembler()->LoadImmediate(temp, Low32Bits(int_value));
+        GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
+        GetAssembler()->LoadImmediate(temp, High32Bits(int_value));
+        GetAssembler()->StoreToOffset(kStoreWord,
+                                      temp,
+                                      sp,
+                                      destination.GetHighStackIndex(kArmWordSize));
+      }
+    } else {
+      DCHECK(constant->IsFloatConstant()) << constant->DebugName();
+      float value = constant->AsFloatConstant()->GetValue();
+      if (destination.IsFpuRegister()) {
+        __ Vmov(F32, destination.AsFpuRegister<vixl32::SRegister>(), value);
+      } else {
+        DCHECK(destination.IsStackSlot());
+        UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+        vixl32::Register temp = temps.Acquire();
+        GetAssembler()->LoadImmediate(temp, bit_cast<int32_t, float>(value));
+        GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
+      }
+    }
+  }
+}
+
+void ParallelMoveResolverARMVIXL::Exchange(Register reg, int mem) {
+  TODO_VIXL32(FATAL);
+}
+
+void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
+  TODO_VIXL32(FATAL);
+}
+
+void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
+  TODO_VIXL32(FATAL);
+}
+
+void ParallelMoveResolverARMVIXL::SpillScratch(int reg ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+}
+
+void ParallelMoveResolverARMVIXL::RestoreScratch(int reg ATTRIBUTE_UNUSED) {
+  TODO_VIXL32(FATAL);
+}
+
+
+// TODO: Remove when codegen complete.
+#pragma GCC diagnostic pop
+
+#undef __
+#undef QUICK_ENTRY_POINT
+#undef TODO_VIXL32
+
+}  // namespace arm
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
new file mode 100644
index 0000000..7b7118c
--- /dev/null
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
+#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
+
+#include "code_generator_arm.h"
+#include "utils/arm/assembler_arm_vixl.h"
+
+// TODO(VIXL): make vixl clean wrt -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "aarch32/constants-aarch32.h"
+#include "aarch32/instructions-aarch32.h"
+#include "aarch32/macro-assembler-aarch32.h"
+#pragma GCC diagnostic pop
+
+// True if VIXL32 should be used for codegen on ARM.
+#ifdef USE_VIXL_ARM_BACKEND
+static constexpr bool kArmUseVIXL32 = true;
+#else
+static constexpr bool kArmUseVIXL32 = false;
+#endif
+
+namespace art {
+namespace arm {
+
+static const vixl::aarch32::Register kMethodRegister = vixl::aarch32::r0;
+static const vixl::aarch32::Register kCoreAlwaysSpillRegister = vixl::aarch32::r5;
+static const vixl::aarch32::RegisterList kCoreCalleeSaves = vixl::aarch32::RegisterList(
+    (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) | (1 << R10) | (1 << R11) | (1 << LR));
+// Callee saves s16 to s31 inc.
+static const vixl::aarch32::SRegisterList kFpuCalleeSaves =
+    vixl::aarch32::SRegisterList(vixl::aarch32::s16, 16);
+
+#define FOR_EACH_IMPLEMENTED_INSTRUCTION(M)     \
+  M(Above)                                      \
+  M(AboveOrEqual)                               \
+  M(Add)                                        \
+  M(Below)                                      \
+  M(BelowOrEqual)                               \
+  M(Div)                                        \
+  M(DivZeroCheck)                               \
+  M(Equal)                                      \
+  M(Exit)                                       \
+  M(Goto)                                       \
+  M(GreaterThan)                                \
+  M(GreaterThanOrEqual)                         \
+  M(If)                                         \
+  M(IntConstant)                                \
+  M(LessThan)                                   \
+  M(LessThanOrEqual)                            \
+  M(LongConstant)                               \
+  M(MemoryBarrier)                              \
+  M(Mul)                                        \
+  M(Not)                                        \
+  M(NotEqual)                                   \
+  M(ParallelMove)                               \
+  M(Return)                                     \
+  M(ReturnVoid)                                 \
+  M(Sub)                                        \
+  M(TypeConversion)                             \
+
+// TODO: Remove once the VIXL32 backend is implemented completely.
+#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)   \
+  M(And)                                        \
+  M(ArrayGet)                                   \
+  M(ArrayLength)                                \
+  M(ArraySet)                                   \
+  M(BooleanNot)                                 \
+  M(BoundsCheck)                                \
+  M(BoundType)                                  \
+  M(CheckCast)                                  \
+  M(ClassTableGet)                              \
+  M(ClearException)                             \
+  M(ClinitCheck)                                \
+  M(Compare)                                    \
+  M(CurrentMethod)                              \
+  M(Deoptimize)                                 \
+  M(DoubleConstant)                             \
+  M(FloatConstant)                              \
+  M(InstanceFieldGet)                           \
+  M(InstanceFieldSet)                           \
+  M(InstanceOf)                                 \
+  M(InvokeInterface)                            \
+  M(InvokeStaticOrDirect)                       \
+  M(InvokeUnresolved)                           \
+  M(InvokeVirtual)                              \
+  M(LoadClass)                                  \
+  M(LoadException)                              \
+  M(LoadString)                                 \
+  M(MonitorOperation)                           \
+  M(NativeDebugInfo)                            \
+  M(Neg)                                        \
+  M(NewArray)                                   \
+  M(NewInstance)                                \
+  M(NullCheck)                                  \
+  M(NullConstant)                               \
+  M(Or)                                         \
+  M(PackedSwitch)                               \
+  M(ParameterValue)                             \
+  M(Phi)                                        \
+  M(Rem)                                        \
+  M(Ror)                                        \
+  M(Select)                                     \
+  M(Shl)                                        \
+  M(Shr)                                        \
+  M(StaticFieldGet)                             \
+  M(StaticFieldSet)                             \
+  M(SuspendCheck)                               \
+  M(Throw)                                      \
+  M(TryBoundary)                                \
+  M(UnresolvedInstanceFieldGet)                 \
+  M(UnresolvedInstanceFieldSet)                 \
+  M(UnresolvedStaticFieldGet)                   \
+  M(UnresolvedStaticFieldSet)                   \
+  M(UShr)                                       \
+  M(Xor)                                        \
+
+class CodeGeneratorARMVIXL;
+
+class SlowPathCodeARMVIXL : public SlowPathCode {
+ public:
+  explicit SlowPathCodeARMVIXL(HInstruction* instruction)
+      : SlowPathCode(instruction), entry_label_(), exit_label_() {}
+
+  vixl::aarch32::Label* GetEntryLabel() { return &entry_label_; }
+  vixl::aarch32::Label* GetExitLabel() { return &exit_label_; }
+
+  void SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) OVERRIDE;
+  void RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) OVERRIDE;
+
+ private:
+  vixl::aarch32::Label entry_label_;
+  vixl::aarch32::Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARMVIXL);
+};
+
+class ParallelMoveResolverARMVIXL : public ParallelMoveResolverWithSwap {
+ public:
+  ParallelMoveResolverARMVIXL(ArenaAllocator* allocator, CodeGeneratorARMVIXL* codegen)
+      : ParallelMoveResolverWithSwap(allocator), codegen_(codegen) {}
+
+  void EmitMove(size_t index) OVERRIDE;
+  void EmitSwap(size_t index) OVERRIDE;
+  void SpillScratch(int reg) OVERRIDE;
+  void RestoreScratch(int reg) OVERRIDE;
+
+  ArmVIXLAssembler* GetAssembler() const;
+
+ private:
+  void Exchange(Register reg, int mem);
+  void Exchange(int mem1, int mem2);
+
+  CodeGeneratorARMVIXL* const codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARMVIXL);
+};
+
+#define DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR(Name)            \
+  void Visit##Name(H##Name*) OVERRIDE;
+
+#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR(Name)          \
+  void Visit##Name(H##Name* instr) OVERRIDE {                   \
+    VisitUnimplemementedInstruction(instr); }
+
+class LocationsBuilderARMVIXL : public HGraphVisitor {
+ public:
+  LocationsBuilderARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen)
+      : HGraphVisitor(graph), codegen_(codegen) {}
+
+  FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+
+  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+
+ private:
+  void VisitUnimplemementedInstruction(HInstruction* instruction) {
+    LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
+  }
+
+  void HandleCondition(HCondition* condition);
+
+  CodeGeneratorARMVIXL* const codegen_;
+  InvokeDexCallingConventionVisitorARM parameter_visitor_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARMVIXL);
+};
+
+class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator {
+ public:
+  InstructionCodeGeneratorARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen);
+
+  FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+
+  FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+
+  ArmVIXLAssembler* GetAssembler() const { return assembler_; }
+  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+
+ private:
+  void VisitUnimplemementedInstruction(HInstruction* instruction) {
+    LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
+  }
+
+  void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
+  void HandleGoto(HInstruction* got, HBasicBlock* successor);
+  void HandleCondition(HCondition* condition);
+  void GenerateTestAndBranch(HInstruction* instruction,
+                             size_t condition_input_index,
+                             vixl::aarch32::Label* true_target,
+                             vixl::aarch32::Label* false_target);
+  void GenerateCompareTestAndBranch(HCondition* condition,
+                                    vixl::aarch32::Label* true_target,
+                                    vixl::aarch32::Label* false_target);
+  void GenerateVcmp(HInstruction* instruction);
+  void GenerateFPJumps(HCondition* cond,
+                       vixl::aarch32::Label* true_label,
+                       vixl::aarch32::Label* false_label);
+  void GenerateLongComparesAndJumps(HCondition* cond,
+                                    vixl::aarch32::Label* true_label,
+                                    vixl::aarch32::Label* false_label);
+  void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+  void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+  void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
+  void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
+
+  ArmVIXLAssembler* const assembler_;
+  CodeGeneratorARMVIXL* const codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorARMVIXL);
+};
+
+class CodeGeneratorARMVIXL : public CodeGenerator {
+ public:
+  CodeGeneratorARMVIXL(HGraph* graph,
+                       const ArmInstructionSetFeatures& isa_features,
+                       const CompilerOptions& compiler_options,
+                       OptimizingCompilerStats* stats = nullptr);
+
+  virtual ~CodeGeneratorARMVIXL() {}
+
+  void Initialize() OVERRIDE {
+    block_labels_.resize(GetGraph()->GetBlocks().size());
+  }
+
+  void GenerateFrameEntry() OVERRIDE;
+  void GenerateFrameExit() OVERRIDE;
+  void Bind(HBasicBlock* block) OVERRIDE;
+  void MoveConstant(Location destination, int32_t value) OVERRIDE;
+  void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+  void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
+  ArmVIXLAssembler* GetAssembler() OVERRIDE { return &assembler_; }
+
+  const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
+
+  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+
+  size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
+
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return vixl::aarch32::kRegSizeInBytes; }
+
+  HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
+
+  HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
+
+  uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE;
+
+  void GenerateMemoryBarrier(MemBarrierKind kind);
+  void Finalize(CodeAllocator* allocator) OVERRIDE;
+  void SetupBlockedRegisters() const OVERRIDE;
+
+  // Blocks all register pairs made out of blocked core registers.
+  void UpdateBlockedPairRegisters() const;
+
+  void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
+  void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+
+  InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kThumb2; }
+
+  const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
+
+  vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
+  // Saves the register in the stack. Returns the size taken on stack.
+  size_t SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                          uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister";
+    return 0;
+  }
+
+  // Restores the register from the stack. Returns the size taken on stack.
+  size_t RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                             uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister";
+    return 0;
+  }
+
+  size_t SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                   uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: SaveFloatingPointRegister";
+    return 0;
+  }
+
+  size_t RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+                                      uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
+    UNIMPLEMENTED(INFO) << "TODO: RestoreFloatingPointRegister";
+    return 0;
+  }
+
+  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+    return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
+  }
+
+  void ComputeSpillMask() OVERRIDE;
+
+  void GenerateImplicitNullCheck(HNullCheck* null_check) OVERRIDE;
+  void GenerateExplicitNullCheck(HNullCheck* null_check) OVERRIDE;
+
+  ParallelMoveResolver* GetMoveResolver() OVERRIDE {
+    return &move_resolver_;
+  }
+
+  // Generate code to invoke a runtime entry point.
+  void InvokeRuntime(QuickEntrypointEnum entrypoint,
+                     HInstruction* instruction,
+                     uint32_t dex_pc,
+                     SlowPathCode* slow_path = nullptr) OVERRIDE;
+
+  // Generate code to invoke a runtime entry point, but do not record
+  // PC-related information in a stack map.
+  void InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
+                                           HInstruction* instruction,
+                                           SlowPathCode* slow_path);
+
+  void GenerateInvokeRuntime(int32_t entry_point_offset);
+
+  // Check if the desired_string_load_kind is supported. If it is, return it,
+  // otherwise return a fall-back kind that should be used instead.
+  HLoadString::LoadKind GetSupportedLoadStringKind(
+      HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
+  // Check if the desired_class_load_kind is supported. If it is, return it,
+  // otherwise return a fall-back kind that should be used instead.
+  HLoadClass::LoadKind GetSupportedLoadClassKind(
+      HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
+  // Check if the desired_dispatch_info is supported. If it is, return it,
+  // otherwise return a fall-back info that should be used instead.
+  HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+      const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
+
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
+  void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+
+  void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
+
+  void GenerateNop() OVERRIDE;
+
+  vixl::aarch32::Label* GetLabelOf(HBasicBlock* block) {
+    block = FirstNonEmptyBlock(block);
+    return &(block_labels_[block->GetBlockId()]);
+  }
+
+ private:
+  // Labels for each block that will be compiled.
+  // We use a deque so that the `vixl::aarch32::Label` objects do not move in memory.
+  ArenaDeque<vixl::aarch32::Label> block_labels_;  // Indexed by block id.
+  vixl::aarch32::Label frame_entry_label_;
+
+  LocationsBuilderARMVIXL location_builder_;
+  InstructionCodeGeneratorARMVIXL instruction_visitor_;
+  ParallelMoveResolverARMVIXL move_resolver_;
+
+  ArmVIXLAssembler assembler_;
+  const ArmInstructionSetFeatures& isa_features_;
+
+  DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
+};
+
+#undef FOR_EACH_IMPLEMENTED_INSTRUCTION
+#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION
+#undef DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR
+#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR
+
+
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index b2e7595..5c0ca85 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -194,10 +194,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     mips_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -2141,15 +2137,13 @@
 }
 
 void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -2664,14 +2658,8 @@
 }
 
 void LocationsBuilderMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -3725,7 +3713,7 @@
 void LocationsBuilderMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -4339,7 +4327,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method ATTRIBUTE_UNUSED) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
   // We disable PC-relative load when there is an irreducible loop, as the optimization
   // is incompatible with it.
@@ -4408,13 +4396,16 @@
   }
 
   switch (method_load_kind) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
+      uint32_t offset =
+          GetThreadOffset<kMipsPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
       __ LoadFromOffset(kLoadWord,
                         temp.AsRegister<Register>(),
                         TR,
-                        invoke->GetStringInitOffset());
+                        offset);
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
@@ -5112,7 +5103,8 @@
 }
 
 void LocationsBuilderMIPS::VisitNullCheck(HNullCheck* instruction) {
-  codegen_->CreateNullCheckLocations(instruction);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
 }
 
 void CodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -5406,7 +5398,7 @@
 void LocationsBuilderMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 553a7e6..f943978 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -395,7 +395,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
   void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 557e5da..02576bd 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -150,10 +150,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     mips64_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -1558,15 +1554,13 @@
 }
 
 void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -2110,14 +2104,8 @@
 }
 
 void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -2630,7 +2618,7 @@
 void LocationsBuilderMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -2984,7 +2972,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method ATTRIBUTE_UNUSED) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   switch (desired_dispatch_info.method_load_kind) {
     case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
@@ -3018,13 +3006,16 @@
 
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
+      uint32_t offset =
+          GetThreadOffset<kMips64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
       __ LoadFromOffset(kLoadDoubleword,
                         temp.AsRegister<GpuRegister>(),
                         TR,
-                        invoke->GetStringInitOffset());
+                        offset);
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
@@ -3461,7 +3452,8 @@
 }
 
 void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) {
-  codegen_->CreateNullCheckLocations(instruction);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  locations->SetInAt(0, Location::RequiresRegister());
 }
 
 void CodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -3741,7 +3733,7 @@
 void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 2dd409a..690eccb 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -343,7 +343,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
   void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 172ce4a..a7051ae 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -84,10 +84,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     x86_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -154,6 +150,9 @@
         length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2));
       }
       __ movl(length_loc.AsRegister<Register>(), array_len);
+      if (mirror::kUseStringCompression) {
+        __ andl(length_loc.AsRegister<Register>(), Immediate(INT32_MAX));
+      }
     }
     x86_codegen->EmitParallelMoves(
         locations->InAt(0),
@@ -1458,7 +1457,7 @@
 void LocationsBuilderX86::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::Any());
   }
@@ -3548,10 +3547,7 @@
 }
 
 void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   switch (instruction->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -3571,9 +3567,6 @@
     default:
       LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
   }
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -4227,7 +4220,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method ATTRIBUTE_UNUSED) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
 
   // We disable pc-relative load when there is an irreducible loop, as the optimization
@@ -4286,10 +4279,13 @@
                                                                   Location temp) {
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
-      __ fs()->movl(temp.AsRegister<Register>(), Address::Absolute(invoke->GetStringInitOffset()));
+      uint32_t offset =
+          GetThreadOffset<kX86PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
+      __ fs()->movl(temp.AsRegister<Register>(), Address::Absolute(offset));
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
@@ -4307,7 +4303,7 @@
       __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -4517,7 +4513,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
 
@@ -4927,11 +4923,11 @@
 }
 
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = codegen_->CreateNullCheckLocations(instruction);
-  if (!codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
-    // Explicit null checks can use any location.
-    locations->SetInAt(0, Location::Any());
-  }
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+      ? Location::RequiresRegister()
+      : Location::Any();
+  locations->SetInAt(0, loc);
 }
 
 void CodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -4978,7 +4974,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -5028,7 +5024,23 @@
 
     case Primitive::kPrimChar: {
       Register out = out_loc.AsRegister<Register>();
-      __ movzxw(out, CodeGeneratorX86::ArrayAddress(obj, index, TIMES_2, data_offset));
+      if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+        // Branch cases into compressed and uncompressed for each index's type.
+        uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+        NearLabel done, not_compressed;
+        __ cmpl(Address(obj, count_offset), Immediate(0));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ j(kGreaterEqual, &not_compressed);
+        __ movzxb(out, CodeGeneratorX86::ArrayAddress(obj, index, TIMES_1, data_offset));
+        __ jmp(&done);
+        __ Bind(&not_compressed);
+        __ movzxw(out, CodeGeneratorX86::ArrayAddress(obj, index, TIMES_2, data_offset));
+        __ Bind(&done);
+      } else {
+        // Common case for charAt of array of char or when string compression's
+        // feature is turned off.
+        __ movzxw(out, CodeGeneratorX86::ArrayAddress(obj, index, TIMES_2, data_offset));
+      }
       break;
     }
 
@@ -5366,24 +5378,32 @@
   Register out = locations->Out().AsRegister<Register>();
   __ movl(out, Address(obj, offset));
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out most significant bit in case the array is String's array of char.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ andl(out, Immediate(INT32_MAX));
+  }
 }
 
 void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   HInstruction* length = instruction->InputAt(1);
   if (!length->IsEmittedAtUseSite()) {
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   }
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
+  // Need register to see array's length.
+  if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+    locations->AddTemp(Location::RequiresRegister());
   }
 }
 
 void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
+  const bool is_string_compressed_char_at =
+      mirror::kUseStringCompression && instruction->IsStringCharAt();
   LocationSummary* locations = instruction->GetLocations();
   Location index_loc = locations->InAt(0);
   Location length_loc = locations->InAt(1);
@@ -5418,13 +5438,23 @@
       uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength());
       Location array_loc = array_length->GetLocations()->InAt(0);
       Address array_len(array_loc.AsRegister<Register>(), len_offset);
-      if (index_loc.IsConstant()) {
-        int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant());
-        __ cmpl(array_len, Immediate(value));
+      if (is_string_compressed_char_at) {
+        Register length_reg = locations->GetTemp(0).AsRegister<Register>();
+        __ movl(length_reg, array_len);
+        codegen_->MaybeRecordImplicitNullCheck(array_length);
+        __ andl(length_reg, Immediate(INT32_MAX));
+        codegen_->GenerateIntCompare(length_reg, index_loc);
       } else {
-        __ cmpl(array_len, index_loc.AsRegister<Register>());
+        // Checking bounds for general case:
+        // Array of char or string's array with feature compression off.
+        if (index_loc.IsConstant()) {
+          int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant());
+          __ cmpl(array_len, Immediate(value));
+        } else {
+          __ cmpl(array_len, index_loc.AsRegister<Register>());
+        }
+        codegen_->MaybeRecordImplicitNullCheck(array_length);
       }
-      codegen_->MaybeRecordImplicitNullCheck(array_length);
     } else {
       codegen_->GenerateIntCompare(length_loc, index_loc);
     }
@@ -5444,7 +5474,7 @@
 void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5802,7 +5832,7 @@
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
 
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
@@ -6099,7 +6129,7 @@
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   if (baker_read_barrier_slow_path) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
@@ -7287,13 +7317,17 @@
 
 void CodeGeneratorX86::GenerateIntCompare(Location lhs, Location rhs) {
   Register lhs_reg = lhs.AsRegister<Register>();
+  GenerateIntCompare(lhs_reg, rhs);
+}
+
+void CodeGeneratorX86::GenerateIntCompare(Register lhs, Location rhs) {
   if (rhs.IsConstant()) {
     int32_t value = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
-    Compare32BitValue(lhs_reg, value);
+    Compare32BitValue(lhs, value);
   } else if (rhs.IsStackSlot()) {
-    __ cmpl(lhs_reg, Address(ESP, rhs.GetStackIndex()));
+    __ cmpl(lhs, Address(ESP, rhs.GetStackIndex()));
   } else {
-    __ cmpl(lhs_reg, rhs.AsRegister<Register>());
+    __ cmpl(lhs, rhs.AsRegister<Register>());
   }
 }
 
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 5866e65..1bd28da 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -402,7 +402,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   // Generate a call to a static or direct method.
   Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
@@ -474,6 +474,7 @@
 
   // Compare int values. Supports only register locations for `lhs`.
   void GenerateIntCompare(Location lhs, Location rhs);
+  void GenerateIntCompare(Register lhs, Location rhs);
 
   // Construct address for array access.
   static Address ArrayAddress(Register obj,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 1943ddc..b243ee0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -88,10 +88,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
-    if (instruction_->CanThrowIntoCatchBlock()) {
-      // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
     x86_64_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
@@ -202,6 +198,9 @@
         length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2));
       }
       __ movl(length_loc.AsRegister<CpuRegister>(), array_len);
+      if (mirror::kUseStringCompression) {
+        __ andl(length_loc.AsRegister<CpuRegister>(), Immediate(INT32_MAX));
+      }
     }
 
     // We're moving two locations to locations that could overlap, so we need a parallel
@@ -737,7 +736,7 @@
 
 HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method ATTRIBUTE_UNUSED) {
+      HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
   switch (desired_dispatch_info.code_ptr_location) {
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
@@ -758,11 +757,13 @@
   // All registers are assumed to be correctly set up.
   Location callee_method = temp;  // For all kinds except kRecursive, callee will be in temp.
   switch (invoke->GetMethodLoadKind()) {
-    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
+    case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
       // temp = thread->string_init_entrypoint
-      __ gs()->movq(temp.AsRegister<CpuRegister>(),
-                    Address::Absolute(invoke->GetStringInitOffset(), /* no_rip */ true));
+      uint32_t offset =
+          GetThreadOffset<kX86_64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
+      __ gs()->movq(temp.AsRegister<CpuRegister>(), Address::Absolute(offset, /* no_rip */ true));
       break;
+    }
     case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
       callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
       break;
@@ -779,7 +780,7 @@
               Address::Absolute(kDummy32BitOffset, /* no_rip */ false));
       // Bind a new fixup label at the end of the "movl" insn.
       uint32_t offset = invoke->GetDexCacheArrayOffset();
-      __ Bind(NewPcRelativeDexCacheArrayPatch(*invoke->GetTargetMethod().dex_file, offset));
+      __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset));
       break;
     }
     case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
@@ -1498,7 +1499,7 @@
 void LocationsBuilderX86_64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::Any());
   }
@@ -3618,14 +3619,8 @@
 }
 
 void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
   locations->SetInAt(0, Location::Any());
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
@@ -4021,7 +4016,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -4396,11 +4391,11 @@
 }
 
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = codegen_->CreateNullCheckLocations(instruction);
-  if (!codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
-    // Explicit null checks can use any location.
-    locations->SetInAt(0, Location::Any());
-  }
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
+  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+      ? Location::RequiresRegister()
+      : Location::Any();
+  locations->SetInAt(0, loc);
 }
 
 void CodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
@@ -4447,7 +4442,7 @@
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
   if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -4493,7 +4488,21 @@
 
     case Primitive::kPrimChar: {
       CpuRegister out = out_loc.AsRegister<CpuRegister>();
-      __ movzxw(out, CodeGeneratorX86_64::ArrayAddress(obj, index, TIMES_2, data_offset));
+      if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+        // Branch cases into compressed and uncompressed for each index's type.
+        uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+        NearLabel done, not_compressed;
+        __ cmpl(Address(obj, count_offset), Immediate(0));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ j(kGreaterEqual, &not_compressed);
+        __ movzxb(out, CodeGeneratorX86_64::ArrayAddress(obj, index, TIMES_1, data_offset));
+        __ jmp(&done);
+        __ Bind(&not_compressed);
+        __ movzxw(out, CodeGeneratorX86_64::ArrayAddress(obj, index, TIMES_2, data_offset));
+        __ Bind(&done);
+      } else {
+        __ movzxw(out, CodeGeneratorX86_64::ArrayAddress(obj, index, TIMES_2, data_offset));
+      }
       break;
     }
 
@@ -4815,21 +4824,23 @@
   CpuRegister out = locations->Out().AsRegister<CpuRegister>();
   __ movl(out, Address(obj, offset));
   codegen_->MaybeRecordImplicitNullCheck(instruction);
+  // Mask out most significant bit in case the array is String's array of char.
+  if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+    __ andl(out, Immediate(INT32_MAX));
+  }
 }
 
 void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
-      ? LocationSummary::kCallOnSlowPath
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  RegisterSet caller_saves = RegisterSet::Empty();
+  InvokeRuntimeCallingConvention calling_convention;
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+  caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
   locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
   HInstruction* length = instruction->InputAt(1);
   if (!length->IsEmittedAtUseSite()) {
     locations->SetInAt(1, Location::RegisterOrConstant(length));
   }
-  if (instruction->HasUses()) {
-    locations->SetOut(Location::SameAsFirstInput());
-  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -4866,13 +4877,23 @@
       uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength());
       Location array_loc = array_length->GetLocations()->InAt(0);
       Address array_len(array_loc.AsRegister<CpuRegister>(), len_offset);
-      if (index_loc.IsConstant()) {
-        int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant());
-        __ cmpl(array_len, Immediate(value));
+      if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+        CpuRegister length_reg = CpuRegister(TMP);
+        __ movl(length_reg, array_len);
+        codegen_->MaybeRecordImplicitNullCheck(array_length);
+        __ andl(length_reg, Immediate(INT32_MAX));
+        codegen_->GenerateIntCompare(length_reg, index_loc);
       } else {
-        __ cmpl(array_len, index_loc.AsRegister<CpuRegister>());
+        // Checking the bound for general case:
+        // Array of char or String's array when the compression feature off.
+        if (index_loc.IsConstant()) {
+          int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant());
+          __ cmpl(array_len, Immediate(value));
+        } else {
+          __ cmpl(array_len, index_loc.AsRegister<CpuRegister>());
+        }
+        codegen_->MaybeRecordImplicitNullCheck(array_length);
       }
-      codegen_->MaybeRecordImplicitNullCheck(array_length);
     } else {
       codegen_->GenerateIntCompare(length_loc, index_loc);
     }
@@ -4912,7 +4933,7 @@
 void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
-  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5213,7 +5234,7 @@
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
   if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
 
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
@@ -5488,7 +5509,7 @@
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   if (baker_read_barrier_slow_path) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
@@ -6535,13 +6556,17 @@
 
 void CodeGeneratorX86_64::GenerateIntCompare(Location lhs, Location rhs) {
   CpuRegister lhs_reg = lhs.AsRegister<CpuRegister>();
+  GenerateIntCompare(lhs_reg, rhs);
+}
+
+void CodeGeneratorX86_64::GenerateIntCompare(CpuRegister lhs, Location rhs) {
   if (rhs.IsConstant()) {
     int32_t value = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
-    Compare32BitValue(lhs_reg, value);
+    Compare32BitValue(lhs, value);
   } else if (rhs.IsStackSlot()) {
-    __ cmpl(lhs_reg, Address(CpuRegister(RSP), rhs.GetStackIndex()));
+    __ cmpl(lhs, Address(CpuRegister(RSP), rhs.GetStackIndex()));
   } else {
-    __ cmpl(lhs_reg, rhs.AsRegister<CpuRegister>());
+    __ cmpl(lhs, rhs.AsRegister<CpuRegister>());
   }
 }
 
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 7108676..8dec44e 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -399,7 +399,7 @@
   // otherwise return a fall-back info that should be used instead.
   HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
       const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
-      MethodReference target_method) OVERRIDE;
+      HInvokeStaticOrDirect* invoke) OVERRIDE;
 
   Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
   void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
@@ -510,8 +510,9 @@
   void Compare32BitValue(CpuRegister dest, int32_t value);
   void Compare64BitValue(CpuRegister dest, int64_t value);
 
-  // Compare int values. Supports only register locations for `lhs`.
+  // Compare int values. Supports register locations for `lhs`.
   void GenerateIntCompare(Location lhs, Location rhs);
+  void GenerateIntCompare(CpuRegister lhs, Location rhs);
 
   // Compare long values. Supports only register locations for `lhs`.
   void GenerateLongCompare(Location lhs, Location rhs);
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 070cbb3..f19faa3 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -41,6 +41,7 @@
 #include "register_allocator_linear_scan.h"
 #include "ssa_liveness_analysis.h"
 #include "utils.h"
+#include "utils/arm/assembler_arm_vixl.h"
 #include "utils/arm/managed_register_arm.h"
 #include "utils/mips/managed_register_mips.h"
 #include "utils/mips64/managed_register_mips64.h"
@@ -48,6 +49,7 @@
 
 #ifdef ART_ENABLE_CODEGEN_arm
 #include "code_generator_arm.h"
+#include "code_generator_arm_vixl.h"
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -117,6 +119,28 @@
     blocked_register_pairs_[arm::R6_R7] = false;
   }
 };
+
+// A way to test the VIXL32-based code generator on ARM. This will replace
+// TestCodeGeneratorARM when the VIXL32-based backend replaces the existing one.
+class TestCodeGeneratorARMVIXL : public arm::CodeGeneratorARMVIXL {
+ public:
+  TestCodeGeneratorARMVIXL(HGraph* graph,
+                           const ArmInstructionSetFeatures& isa_features,
+                           const CompilerOptions& compiler_options)
+      : arm::CodeGeneratorARMVIXL(graph, isa_features, compiler_options) {
+    AddAllocatedRegister(Location::RegisterLocation(arm::R6));
+    AddAllocatedRegister(Location::RegisterLocation(arm::R7));
+  }
+
+  void SetupBlockedRegisters() const OVERRIDE {
+    arm::CodeGeneratorARMVIXL::SetupBlockedRegisters();
+    blocked_core_registers_[arm::R4] = true;
+    blocked_core_registers_[arm::R6] = false;
+    blocked_core_registers_[arm::R7] = false;
+    // Makes pair R6-R7 available.
+    blocked_register_pairs_[arm::R6_R7] = false;
+  }
+};
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_x86
@@ -296,6 +320,13 @@
                                                       *features_arm.get(),
                                                       compiler_options);
 }
+
+CodeGenerator* create_codegen_arm_vixl32(HGraph* graph, const CompilerOptions& compiler_options) {
+  std::unique_ptr<const ArmInstructionSetFeatures> features_arm(
+      ArmInstructionSetFeatures::FromCppDefines());
+  return new (graph->GetArena())
+      TestCodeGeneratorARMVIXL(graph, *features_arm.get(), compiler_options);
+}
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_arm64
@@ -351,6 +382,7 @@
 #ifdef ART_ENABLE_CODEGEN_arm
     CodegenTargetConfig(kArm, create_codegen_arm),
     CodegenTargetConfig(kThumb2, create_codegen_arm),
+    CodegenTargetConfig(kArm, create_codegen_arm_vixl32),
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
     CodegenTargetConfig(kArm64, create_codegen_arm64),
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
new file mode 100644
index 0000000..8535417
--- /dev/null
+++ b/compiler/optimizing/common_arm.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+
+// TODO(VIXL): Make VIXL compile with -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "aarch32/macro-assembler-aarch32.h"
+#pragma GCC diagnostic pop
+
+namespace art {
+namespace arm {
+namespace helpers {
+
+static_assert(vixl::aarch32::kSpCode == SP, "vixl::aarch32::kSpCode must equal ART's SP");
+
+inline dwarf::Reg DWARFReg(vixl::aarch32::Register reg) {
+  return dwarf::Reg::ArmCore(static_cast<int>(reg.GetCode()));
+}
+
+inline dwarf::Reg DWARFReg(vixl::aarch32::SRegister reg) {
+  return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode()));
+}
+
+inline vixl::aarch32::DRegister FromLowSToD(vixl::aarch32::SRegister reg) {
+  DCHECK_EQ(reg.GetCode() % 2, 0u) << reg;
+  return vixl::aarch32::DRegister(reg.GetCode() / 2);
+}
+
+inline vixl::aarch32::Register RegisterFrom(Location location) {
+  DCHECK(location.IsRegister()) << location;
+  return vixl::aarch32::Register(location.reg());
+}
+
+inline vixl::aarch32::Register RegisterFrom(Location location, Primitive::Type type) {
+  DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type)) << type;
+  return RegisterFrom(location);
+}
+
+inline vixl::aarch32::DRegister DRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister()) << location;
+  return vixl::aarch32::DRegister(location.reg());
+}
+
+inline vixl::aarch32::SRegister SRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister()) << location;
+  return vixl::aarch32::SRegister(location.reg());
+}
+
+inline vixl::aarch32::SRegister OutputSRegister(HInstruction* instr) {
+  Primitive::Type type = instr->GetType();
+  DCHECK_EQ(type, Primitive::kPrimFloat) << type;
+  return SRegisterFrom(instr->GetLocations()->Out());
+}
+
+inline vixl::aarch32::DRegister OutputDRegister(HInstruction* instr) {
+  Primitive::Type type = instr->GetType();
+  DCHECK_EQ(type, Primitive::kPrimDouble) << type;
+  return DRegisterFrom(instr->GetLocations()->Out());
+}
+
+inline vixl::aarch32::SRegister InputSRegisterAt(HInstruction* instr, int input_index) {
+  Primitive::Type type = instr->InputAt(input_index)->GetType();
+  DCHECK_EQ(type, Primitive::kPrimFloat) << type;
+  return SRegisterFrom(instr->GetLocations()->InAt(input_index));
+}
+
+inline vixl::aarch32::DRegister InputDRegisterAt(HInstruction* instr, int input_index) {
+  Primitive::Type type = instr->InputAt(input_index)->GetType();
+  DCHECK_EQ(type, Primitive::kPrimDouble) << type;
+  return DRegisterFrom(instr->GetLocations()->InAt(input_index));
+}
+
+inline vixl::aarch32::Register OutputRegister(HInstruction* instr) {
+  return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+inline vixl::aarch32::Register InputRegisterAt(HInstruction* instr, int input_index) {
+  return RegisterFrom(instr->GetLocations()->InAt(input_index),
+                      instr->InputAt(input_index)->GetType());
+}
+
+inline int64_t Int64ConstantFrom(Location location) {
+  HConstant* instr = location.GetConstant();
+  if (instr->IsIntConstant()) {
+    return instr->AsIntConstant()->GetValue();
+  } else if (instr->IsNullConstant()) {
+    return 0;
+  } else {
+    DCHECK(instr->IsLongConstant()) << instr->DebugName();
+    return instr->AsLongConstant()->GetValue();
+  }
+}
+
+inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type type) {
+  if (location.IsRegister()) {
+    return vixl::aarch32::Operand(RegisterFrom(location, type));
+  } else {
+    return vixl::aarch32::Operand(Int64ConstantFrom(location));
+  }
+}
+
+inline vixl::aarch32::Operand InputOperandAt(HInstruction* instr, int input_index) {
+  return OperandFrom(instr->GetLocations()->InAt(input_index),
+                     instr->InputAt(input_index)->GetType());
+}
+
+}  // namespace helpers
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index eda0971..776a483 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -273,9 +273,9 @@
 // only SP/WSP and ZXR/WZR codes are different between art and vixl.
 // Note: This function is only used for debug checks.
 inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers,
-                                                   size_t num_core,
-                                                   uint32_t art_fpu_registers,
-                                                   size_t num_fpu) {
+                                            size_t num_core,
+                                            uint32_t art_fpu_registers,
+                                            size_t num_fpu) {
   // The register masks won't work if the number of register is larger than 32.
   DCHECK_GE(sizeof(art_core_registers) * 8, num_core);
   DCHECK_GE(sizeof(art_fpu_registers) * 8, num_fpu);
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 6ad9b07..7010171 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -82,12 +82,10 @@
     // we need to add the dex cache arrays base as the special input.
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARM>(invoke, codegen_)) {
-      // Initialize base for target method dex file if needed.
-      MethodReference target_method = invoke->GetTargetMethod();
-      HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(*target_method.dex_file);
+      HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kArmPointerSize, target_method.dex_file);
-      base->UpdateElementOffset(layout.MethodOffset(target_method.dex_method_index));
+      DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile());
+      base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
       invoke->AddSpecialInput(base);
diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc
index 300284d..4456b49 100644
--- a/compiler/optimizing/dex_cache_array_fixups_mips.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc
@@ -89,11 +89,10 @@
     if (invoke->HasPcRelativeDexCache() &&
         !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) {
       // Initialize base for target method dex file if needed.
-      MethodReference target_method = invoke->GetTargetMethod();
-      HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(*target_method.dex_file);
+      HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
       // Update the element offset in base.
-      DexCacheArraysLayout layout(kMipsPointerSize, target_method.dex_file);
-      base->UpdateElementOffset(layout.MethodOffset(target_method.dex_method_index));
+      DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFile());
+      base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
       // Add the special argument base to the method.
       DCHECK(!invoke->HasCurrentMethodInput());
       invoke->AddSpecialInput(base);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index b3d5341..912ee29 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -447,7 +447,7 @@
 
   void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE {
     VisitInvoke(invoke);
-    StartAttributeStream("invoke_type") << invoke->GetOriginalInvokeType();
+    StartAttributeStream("invoke_type") << invoke->GetInvokeType();
   }
 
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index ce53134..af2fe9c 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -41,7 +41,7 @@
 #include "sharpening.h"
 #include "ssa_builder.h"
 #include "ssa_phi_elimination.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
@@ -263,42 +263,24 @@
     return false;  // Don't bother to move further if we know the method is unresolved.
   }
 
-  uint32_t method_index = invoke_instruction->GetDexMethodIndex();
   ScopedObjectAccess soa(Thread::Current());
+  uint32_t method_index = invoke_instruction->GetDexMethodIndex();
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
   VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file);
 
-  ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   // We can query the dex cache directly. The verifier has populated it already.
-  ArtMethod* resolved_method;
+  ArtMethod* resolved_method = invoke_instruction->GetResolvedMethod();
   ArtMethod* actual_method = nullptr;
-  if (invoke_instruction->IsInvokeStaticOrDirect()) {
-    if (invoke_instruction->AsInvokeStaticOrDirect()->IsStringInit()) {
-      VLOG(compiler) << "Not inlining a String.<init> method";
-      return false;
-    }
-    MethodReference ref = invoke_instruction->AsInvokeStaticOrDirect()->GetTargetMethod();
-    mirror::DexCache* const dex_cache = IsSameDexFile(caller_dex_file, *ref.dex_file)
-        ? caller_compilation_unit_.GetDexCache().Get()
-        : class_linker->FindDexCache(soa.Self(), *ref.dex_file);
-    resolved_method = dex_cache->GetResolvedMethod(
-        ref.dex_method_index, class_linker->GetImagePointerSize());
-    // actual_method == resolved_method for direct or static calls.
+  if (resolved_method == nullptr) {
+    DCHECK(invoke_instruction->IsInvokeStaticOrDirect());
+    DCHECK(invoke_instruction->AsInvokeStaticOrDirect()->IsStringInit());
+    VLOG(compiler) << "Not inlining a String.<init> method";
+    return false;
+  } else if (invoke_instruction->IsInvokeStaticOrDirect()) {
     actual_method = resolved_method;
   } else {
-    resolved_method = caller_compilation_unit_.GetDexCache().Get()->GetResolvedMethod(
-        method_index, class_linker->GetImagePointerSize());
-    if (resolved_method != nullptr) {
-      // Check if we can statically find the method.
-      actual_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method);
-    }
-  }
-
-  if (resolved_method == nullptr) {
-    // TODO: Can this still happen?
-    // Method cannot be resolved if it is in another dex file we do not have access to.
-    VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file);
-    return false;
+    // Check if we can statically find the method.
+    actual_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method);
   }
 
   if (actual_method != nullptr) {
@@ -763,9 +745,9 @@
       // 2) We will not go to the conflict trampoline with an invoke-virtual.
       // TODO: Consider sharpening once it is not dependent on the compiler driver.
       const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
-      uint32_t method_index = FindMethodIndexIn(
+      uint32_t dex_method_index = FindMethodIndexIn(
           method, caller_dex_file, invoke_instruction->GetDexMethodIndex());
-      if (method_index == DexFile::kDexNoIndex) {
+      if (dex_method_index == DexFile::kDexNoIndex) {
         return false;
       }
       HInvokeVirtual* new_invoke = new (graph_->GetArena()) HInvokeVirtual(
@@ -773,7 +755,8 @@
           invoke_instruction->GetNumberOfArguments(),
           invoke_instruction->GetType(),
           invoke_instruction->GetDexPc(),
-          method_index,
+          dex_method_index,
+          method,
           method->GetMethodIndex());
       HInputsRef inputs = invoke_instruction->GetInputs();
       for (size_t index = 0; index != inputs.size(); ++index) {
@@ -1122,7 +1105,7 @@
     }
   }
 
-  InvokeType invoke_type = invoke_instruction->GetOriginalInvokeType();
+  InvokeType invoke_type = invoke_instruction->GetInvokeType();
   if (invoke_type == kInterface) {
     // We have statically resolved the dispatch. To please the class linker
     // at runtime, we change this call as if it was a virtual call.
@@ -1338,7 +1321,7 @@
   HConstantFolding fold(callee_graph);
   HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
   InstructionSimplifier simplify(callee_graph, stats_);
-  IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
+  IntrinsicsRecognizer intrinsics(callee_graph, stats_);
 
   HOptimization* optimizations[] = {
     &intrinsics,
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index d7e4c53..f7d67db 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -21,7 +21,8 @@
 #include "class_linker.h"
 #include "dex_instruction-inl.h"
 #include "driver/compiler_options.h"
-#include "scoped_thread_state_change.h"
+#include "imtable-inl.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -674,7 +675,7 @@
 
   ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
   // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
   // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
@@ -767,6 +768,11 @@
   return resolved_method;
 }
 
+static bool IsStringConstructor(ArtMethod* method) {
+  ScopedObjectAccess soa(Thread::Current());
+  return method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+}
+
 bool HInstructionBuilder::BuildInvoke(const Instruction& instruction,
                                       uint32_t dex_pc,
                                       uint32_t method_idx,
@@ -785,40 +791,6 @@
     number_of_arguments++;
   }
 
-  MethodReference target_method(dex_file_, method_idx);
-
-  // Special handling for string init.
-  int32_t string_init_offset = 0;
-  bool is_string_init = compiler_driver_->IsStringInit(method_idx,
-                                                       dex_file_,
-                                                       &string_init_offset);
-  // Replace calls to String.<init> with StringFactory.
-  if (is_string_init) {
-    HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
-        HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
-        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
-        dchecked_integral_cast<uint64_t>(string_init_offset),
-        0U
-    };
-    HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
-        arena_,
-        number_of_arguments - 1,
-        Primitive::kPrimNot /*return_type */,
-        dex_pc,
-        method_idx,
-        target_method,
-        dispatch_info,
-        invoke_type,
-        kStatic /* optimized_invoke_type */,
-        HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
-    return HandleStringInit(invoke,
-                            number_of_vreg_arguments,
-                            args,
-                            register_index,
-                            is_range,
-                            descriptor);
-  }
-
   ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
 
   if (UNLIKELY(resolved_method == nullptr)) {
@@ -839,6 +811,35 @@
                         true /* is_unresolved */);
   }
 
+  // Replace calls to String.<init> with StringFactory.
+  if (IsStringConstructor(resolved_method)) {
+    uint32_t string_init_entry_point = WellKnownClasses::StringInitToEntryPoint(resolved_method);
+    HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+        HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
+        HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+        dchecked_integral_cast<uint64_t>(string_init_entry_point),
+        0U
+    };
+    MethodReference target_method(dex_file_, method_idx);
+    HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
+        arena_,
+        number_of_arguments - 1,
+        Primitive::kPrimNot /*return_type */,
+        dex_pc,
+        method_idx,
+        nullptr,
+        dispatch_info,
+        invoke_type,
+        target_method,
+        HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
+    return HandleStringInit(invoke,
+                            number_of_vreg_arguments,
+                            args,
+                            register_index,
+                            is_range,
+                            descriptor);
+  }
+
   // Potential class initialization check, in the case of a static method call.
   HClinitCheck* clinit_check = nullptr;
   HInvoke* invoke = nullptr;
@@ -853,10 +854,9 @@
           dex_pc, resolved_method, method_idx, &clinit_check_requirement);
     } else if (invoke_type == kSuper) {
       if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
-        // Update the target method to the one resolved. Note that this may be a no-op if
+        // Update the method index to the one resolved. Note that this may be a no-op if
         // we resolved to the method referenced by the instruction.
         method_idx = resolved_method->GetDexMethodIndex();
-        target_method = MethodReference(dex_file_, method_idx);
       }
     }
 
@@ -866,15 +866,17 @@
         0u,
         0U
     };
+    MethodReference target_method(resolved_method->GetDexFile(),
+                                  resolved_method->GetDexMethodIndex());
     invoke = new (arena_) HInvokeStaticOrDirect(arena_,
                                                 number_of_arguments,
                                                 return_type,
                                                 dex_pc,
                                                 method_idx,
-                                                target_method,
+                                                resolved_method,
                                                 dispatch_info,
                                                 invoke_type,
-                                                invoke_type,
+                                                target_method,
                                                 clinit_check_requirement);
   } else if (invoke_type == kVirtual) {
     ScopedObjectAccess soa(Thread::Current());  // Needed for the method index
@@ -883,16 +885,18 @@
                                          return_type,
                                          dex_pc,
                                          method_idx,
+                                         resolved_method,
                                          resolved_method->GetMethodIndex());
   } else {
     DCHECK_EQ(invoke_type, kInterface);
-    ScopedObjectAccess soa(Thread::Current());  // Needed for the method index
+    ScopedObjectAccess soa(Thread::Current());  // Needed for the IMT index.
     invoke = new (arena_) HInvokeInterface(arena_,
                                            number_of_arguments,
                                            return_type,
                                            dex_pc,
                                            method_idx,
-                                           resolved_method->GetImtIndex());
+                                           resolved_method,
+                                           ImTable::GetImtIndex(resolved_method));
   }
 
   return HandleInvoke(invoke,
@@ -1103,7 +1107,7 @@
 
   size_t start_index = 0;
   size_t argument_index = 0;
-  if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) {  // Instance call.
+  if (invoke->GetInvokeType() != InvokeType::kStatic) {  // Instance call.
     uint32_t obj_reg = is_range ? register_index : args[0];
     HInstruction* arg = is_unresolved
         ? LoadLocal(obj_reg, Primitive::kPrimNot)
@@ -1280,7 +1284,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
+      soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader())));
   Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache();
 
   return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
@@ -1299,7 +1303,7 @@
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
       soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
@@ -1340,7 +1344,7 @@
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+      soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
   ArtField* resolved_field = compiler_driver_->ResolveField(
       soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
 
@@ -1813,7 +1817,20 @@
     case Instruction::MOVE_OBJECT:
     case Instruction::MOVE_OBJECT_16:
     case Instruction::MOVE_OBJECT_FROM16: {
-      HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot);
+      // The verifier has no notion of a null type, so a move-object of constant 0
+      // will lead to the same constant 0 in the destination register. To mimic
+      // this behavior, we just pretend we haven't seen a type change (int to reference)
+      // for the 0 constant and phis. We rely on our type propagation to eventually get the
+      // types correct.
+      uint32_t reg_number = instruction.VRegB();
+      HInstruction* value = (*current_locals_)[reg_number];
+      if (value->IsIntConstant()) {
+        DCHECK_EQ(value->AsIntConstant()->GetValue(), 0);
+      } else if (value->IsPhi()) {
+        DCHECK(value->GetType() == Primitive::kPrimInt || value->GetType() == Primitive::kPrimNot);
+      } else {
+        value = LoadLocal(reg_number, Primitive::kPrimNot);
+      }
       UpdateLocal(instruction.VRegA(), value);
       break;
     }
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 4ca0600..3bb1c1d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -18,7 +18,7 @@
 
 #include "intrinsics.h"
 #include "mirror/class-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -1577,6 +1577,18 @@
     return;
   }
 
+  if ((input_cst != nullptr) && input_cst->IsOne()
+      && input_other->GetType() == Primitive::kPrimBoolean) {
+    // Replace code looking like
+    //    XOR dst, src, 1
+    // with
+    //    BOOLEAN_NOT dst, src
+    HBooleanNot* boolean_not = new (GetGraph()->GetArena()) HBooleanNot(input_other);
+    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, boolean_not);
+    RecordSimplification();
+    return;
+  }
+
   if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) {
     // Replace code looking like
     //    XOR dst, src, 0xFFF...FF
@@ -1645,7 +1657,7 @@
                                                   bool is_left,
                                                   Primitive::Type type) {
   DCHECK(invoke->IsInvokeStaticOrDirect());
-  DCHECK_EQ(invoke->GetOriginalInvokeType(), InvokeType::kStatic);
+  DCHECK_EQ(invoke->GetInvokeType(), InvokeType::kStatic);
   HInstruction* value = invoke->InputAt(0);
   HInstruction* distance = invoke->InputAt(1);
   // Replace the invoke with an HRor.
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 495f3fd..56e4c7a 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -44,6 +44,14 @@
   size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
   Primitive::Type type = instruction->GetType();
 
+  // TODO: Implement reading (length + compression) for String compression feature from
+  // negative offset (count_offset - data_offset). Thumb2Assembler does not support T4
+  // encoding of "LDR (immediate)" at the moment.
+  // Don't move array pointer if it is charAt because we need to take the count first.
+  if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+    return;
+  }
+
   if (type == Primitive::kPrimLong
       || type == Primitive::kPrimFloat
       || type == Primitive::kPrimDouble) {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 6d107d5..d0dd650 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -140,6 +140,13 @@
 
 void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
   size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+  // Don't move the array pointer if it is charAt because we need to take the count first.
+  // TODO: Implement reading (length + compression) for String compression feature from
+  // negative offset (count_offset - data_offset) using LDP and clobbering an extra temporary.
+  // Note that "LDR (Immediate)" does not have a "signed offset" encoding.
+  if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
+    return;
+  }
   if (TryExtractArrayAccessAddress(instruction,
                                    instruction->GetArray(),
                                    instruction->GetIndex(),
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 418d59c..412ccfc 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -18,14 +18,11 @@
 
 #include "art_method.h"
 #include "class_linker.h"
-#include "dex/quick/dex_file_method_inliner.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
 #include "invoke_type.h"
 #include "mirror/dex_cache-inl.h"
 #include "nodes.h"
-#include "quick/inline_method_analyser.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
 
@@ -36,7 +33,7 @@
   switch (i) {
     case Intrinsics::kNone:
       return kInterface;  // Non-sensical for intrinsic.
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     case Intrinsics::k ## Name: \
       return IsStatic;
 #include "intrinsics_list.h"
@@ -52,7 +49,7 @@
   switch (i) {
     case Intrinsics::kNone:
       return kNeedsEnvironmentOrCache;  // Non-sensical for intrinsic.
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     case Intrinsics::k ## Name: \
       return NeedsEnvironmentOrCache;
 #include "intrinsics_list.h"
@@ -68,7 +65,7 @@
   switch (i) {
     case Intrinsics::kNone:
       return kAllSideEffects;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     case Intrinsics::k ## Name: \
       return SideEffects;
 #include "intrinsics_list.h"
@@ -84,7 +81,7 @@
   switch (i) {
     case Intrinsics::kNone:
       return kCanThrow;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     case Intrinsics::k ## Name: \
       return Exceptions;
 #include "intrinsics_list.h"
@@ -95,430 +92,7 @@
   return kCanThrow;
 }
 
-static Primitive::Type GetType(uint64_t data, bool is_op_size) {
-  if (is_op_size) {
-    switch (static_cast<OpSize>(data)) {
-      case kSignedByte:
-        return Primitive::kPrimByte;
-      case kSignedHalf:
-        return Primitive::kPrimShort;
-      case k32:
-        return Primitive::kPrimInt;
-      case k64:
-        return Primitive::kPrimLong;
-      default:
-        LOG(FATAL) << "Unknown/unsupported op size " << data;
-        UNREACHABLE();
-    }
-  } else {
-    if ((data & kIntrinsicFlagIsLong) != 0) {
-      return Primitive::kPrimLong;
-    }
-    if ((data & kIntrinsicFlagIsObject) != 0) {
-      return Primitive::kPrimNot;
-    }
-    return Primitive::kPrimInt;
-  }
-}
-
-static Intrinsics GetIntrinsic(InlineMethod method) {
-  switch (method.opcode) {
-    // Floating-point conversions.
-    case kIntrinsicDoubleCvt:
-      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
-          Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
-    case kIntrinsicFloatCvt:
-      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
-          Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
-    case kIntrinsicFloat2Int:
-      return Intrinsics::kFloatFloatToIntBits;
-    case kIntrinsicDouble2Long:
-      return Intrinsics::kDoubleDoubleToLongBits;
-
-    // Floating-point tests.
-    case kIntrinsicFloatIsInfinite:
-      return Intrinsics::kFloatIsInfinite;
-    case kIntrinsicDoubleIsInfinite:
-      return Intrinsics::kDoubleIsInfinite;
-    case kIntrinsicFloatIsNaN:
-      return Intrinsics::kFloatIsNaN;
-    case kIntrinsicDoubleIsNaN:
-      return Intrinsics::kDoubleIsNaN;
-
-    // Bit manipulations.
-    case kIntrinsicReverseBits:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerReverse;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongReverse;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicReverseBytes:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimShort:
-          return Intrinsics::kShortReverseBytes;
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerReverseBytes;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongReverseBytes;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicRotateRight:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerRotateRight;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongRotateRight;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicRotateLeft:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerRotateLeft;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongRotateLeft;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-
-    // Misc data processing.
-    case kIntrinsicBitCount:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerBitCount;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongBitCount;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicCompare:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerCompare;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongCompare;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicHighestOneBit:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerHighestOneBit;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongHighestOneBit;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicLowestOneBit:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerLowestOneBit;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongLowestOneBit;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicNumberOfLeadingZeros:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerNumberOfLeadingZeros;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongNumberOfLeadingZeros;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicNumberOfTrailingZeros:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerNumberOfTrailingZeros;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongNumberOfTrailingZeros;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicSignum:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimInt:
-          return Intrinsics::kIntegerSignum;
-        case Primitive::kPrimLong:
-          return Intrinsics::kLongSignum;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-
-    // Abs.
-    case kIntrinsicAbsDouble:
-      return Intrinsics::kMathAbsDouble;
-    case kIntrinsicAbsFloat:
-      return Intrinsics::kMathAbsFloat;
-    case kIntrinsicAbsInt:
-      return Intrinsics::kMathAbsInt;
-    case kIntrinsicAbsLong:
-      return Intrinsics::kMathAbsLong;
-
-    // Min/max.
-    case kIntrinsicMinMaxDouble:
-      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
-          Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
-    case kIntrinsicMinMaxFloat:
-      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
-          Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
-    case kIntrinsicMinMaxInt:
-      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
-          Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
-    case kIntrinsicMinMaxLong:
-      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
-          Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
-
-    // More math builtins.
-    case kIntrinsicCos:
-      return Intrinsics::kMathCos;
-    case kIntrinsicSin:
-      return Intrinsics::kMathSin;
-    case kIntrinsicAcos:
-      return Intrinsics::kMathAcos;
-    case kIntrinsicAsin:
-      return Intrinsics::kMathAsin;
-    case kIntrinsicAtan:
-      return Intrinsics::kMathAtan;
-    case kIntrinsicAtan2:
-      return Intrinsics::kMathAtan2;
-    case kIntrinsicCbrt:
-      return Intrinsics::kMathCbrt;
-    case kIntrinsicCosh:
-      return Intrinsics::kMathCosh;
-    case kIntrinsicExp:
-      return Intrinsics::kMathExp;
-    case kIntrinsicExpm1:
-      return Intrinsics::kMathExpm1;
-    case kIntrinsicHypot:
-      return Intrinsics::kMathHypot;
-    case kIntrinsicLog:
-      return Intrinsics::kMathLog;
-    case kIntrinsicLog10:
-      return Intrinsics::kMathLog10;
-    case kIntrinsicNextAfter:
-      return Intrinsics::kMathNextAfter;
-    case kIntrinsicSinh:
-      return Intrinsics::kMathSinh;
-    case kIntrinsicTan:
-      return Intrinsics::kMathTan;
-    case kIntrinsicTanh:
-      return Intrinsics::kMathTanh;
-
-    // Misc math.
-    case kIntrinsicSqrt:
-      return Intrinsics::kMathSqrt;
-    case kIntrinsicCeil:
-      return Intrinsics::kMathCeil;
-    case kIntrinsicFloor:
-      return Intrinsics::kMathFloor;
-    case kIntrinsicRint:
-      return Intrinsics::kMathRint;
-    case kIntrinsicRoundDouble:
-      return Intrinsics::kMathRoundDouble;
-    case kIntrinsicRoundFloat:
-      return Intrinsics::kMathRoundFloat;
-
-    // System.arraycopy.
-    case kIntrinsicSystemArrayCopyCharArray:
-      return Intrinsics::kSystemArrayCopyChar;
-
-    case kIntrinsicSystemArrayCopy:
-      return Intrinsics::kSystemArrayCopy;
-
-    // Thread.currentThread.
-    case kIntrinsicCurrentThread:
-      return Intrinsics::kThreadCurrentThread;
-
-    // Memory.peek.
-    case kIntrinsicPeek:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimByte:
-          return Intrinsics::kMemoryPeekByte;
-        case Primitive::kPrimShort:
-          return Intrinsics::kMemoryPeekShortNative;
-        case Primitive::kPrimInt:
-          return Intrinsics::kMemoryPeekIntNative;
-        case Primitive::kPrimLong:
-          return Intrinsics::kMemoryPeekLongNative;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-
-    // Memory.poke.
-    case kIntrinsicPoke:
-      switch (GetType(method.d.data, true)) {
-        case Primitive::kPrimByte:
-          return Intrinsics::kMemoryPokeByte;
-        case Primitive::kPrimShort:
-          return Intrinsics::kMemoryPokeShortNative;
-        case Primitive::kPrimInt:
-          return Intrinsics::kMemoryPokeIntNative;
-        case Primitive::kPrimLong:
-          return Intrinsics::kMemoryPokeLongNative;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-
-    // String.
-    case kIntrinsicCharAt:
-      return Intrinsics::kStringCharAt;
-    case kIntrinsicCompareTo:
-      return Intrinsics::kStringCompareTo;
-    case kIntrinsicEquals:
-      return Intrinsics::kStringEquals;
-    case kIntrinsicGetCharsNoCheck:
-      return Intrinsics::kStringGetCharsNoCheck;
-    case kIntrinsicIsEmptyOrLength:
-      return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ?
-          Intrinsics::kStringLength : Intrinsics::kStringIsEmpty;
-    case kIntrinsicIndexOf:
-      return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
-          Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
-    case kIntrinsicNewStringFromBytes:
-      return Intrinsics::kStringNewStringFromBytes;
-    case kIntrinsicNewStringFromChars:
-      return Intrinsics::kStringNewStringFromChars;
-    case kIntrinsicNewStringFromString:
-      return Intrinsics::kStringNewStringFromString;
-
-    case kIntrinsicCas:
-      switch (GetType(method.d.data, false)) {
-        case Primitive::kPrimNot:
-          return Intrinsics::kUnsafeCASObject;
-        case Primitive::kPrimInt:
-          return Intrinsics::kUnsafeCASInt;
-        case Primitive::kPrimLong:
-          return Intrinsics::kUnsafeCASLong;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    case kIntrinsicUnsafeGet: {
-      const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
-      switch (GetType(method.d.data, false)) {
-        case Primitive::kPrimInt:
-          return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
-        case Primitive::kPrimLong:
-          return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
-        case Primitive::kPrimNot:
-          return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-    }
-    case kIntrinsicUnsafePut: {
-      enum Sync { kNoSync, kVolatile, kOrdered };
-      const Sync sync =
-          ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
-          ((method.d.data & kIntrinsicFlagIsOrdered) != 0)  ? kOrdered :
-                                                              kNoSync;
-      switch (GetType(method.d.data, false)) {
-        case Primitive::kPrimInt:
-          switch (sync) {
-            case kNoSync:
-              return Intrinsics::kUnsafePut;
-            case kVolatile:
-              return Intrinsics::kUnsafePutVolatile;
-            case kOrdered:
-              return Intrinsics::kUnsafePutOrdered;
-          }
-          break;
-        case Primitive::kPrimLong:
-          switch (sync) {
-            case kNoSync:
-              return Intrinsics::kUnsafePutLong;
-            case kVolatile:
-              return Intrinsics::kUnsafePutLongVolatile;
-            case kOrdered:
-              return Intrinsics::kUnsafePutLongOrdered;
-          }
-          break;
-        case Primitive::kPrimNot:
-          switch (sync) {
-            case kNoSync:
-              return Intrinsics::kUnsafePutObject;
-            case kVolatile:
-              return Intrinsics::kUnsafePutObjectVolatile;
-            case kOrdered:
-              return Intrinsics::kUnsafePutObjectOrdered;
-          }
-          break;
-        default:
-          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
-          UNREACHABLE();
-      }
-      break;
-    }
-
-    // 1.8.
-    case kIntrinsicUnsafeGetAndAddInt:
-      return Intrinsics::kUnsafeGetAndAddInt;
-    case kIntrinsicUnsafeGetAndAddLong:
-      return Intrinsics::kUnsafeGetAndAddLong;
-    case kIntrinsicUnsafeGetAndSetInt:
-      return Intrinsics::kUnsafeGetAndSetInt;
-    case kIntrinsicUnsafeGetAndSetLong:
-      return Intrinsics::kUnsafeGetAndSetLong;
-    case kIntrinsicUnsafeGetAndSetObject:
-      return Intrinsics::kUnsafeGetAndSetObject;
-    case kIntrinsicUnsafeLoadFence:
-      return Intrinsics::kUnsafeLoadFence;
-    case kIntrinsicUnsafeStoreFence:
-      return Intrinsics::kUnsafeStoreFence;
-    case kIntrinsicUnsafeFullFence:
-      return Intrinsics::kUnsafeFullFence;
-
-    // Virtual cases.
-
-    case kIntrinsicReferenceGetReferent:
-      return Intrinsics::kReferenceGetReferent;
-
-    // Quick inliner cases. Remove after refactoring. They are here so that we can use the
-    // compiler to warn on missing cases.
-
-    case kInlineOpNop:
-    case kInlineOpReturnArg:
-    case kInlineOpNonWideConst:
-    case kInlineOpIGet:
-    case kInlineOpIPut:
-    case kInlineOpConstructor:
-      return Intrinsics::kNone;
-
-    // String init cases, not intrinsics.
-
-    case kInlineStringInit:
-      return Intrinsics::kNone;
-
-    // No default case to make the compiler warn on missing cases.
-  }
-  return Intrinsics::kNone;
-}
-
-static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
-  // The DexFileMethodInliner should have checked whether the methods are agreeing with
-  // what we expect, i.e., static methods are called as such. Add another check here for
-  // our expectations:
-  //
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
   // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
   //
   // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
@@ -532,9 +106,7 @@
   // inline. If the precise type is known, however, the instruction will be sharpened to an
   // InvokeStaticOrDirect.
   InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
-  InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
-      invoke->AsInvokeStaticOrDirect()->GetOptimizedInvokeType() :
-      invoke->IsInvokeVirtual() ? kVirtual : kSuper;
+  InvokeType invoke_type = invoke->GetInvokeType();
   switch (intrinsic_type) {
     case kStatic:
       return (invoke_type == kStatic);
@@ -544,13 +116,9 @@
         return true;
       }
       if (invoke_type == kVirtual) {
-        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+        ArtMethod* art_method = invoke->GetResolvedMethod();
         ScopedObjectAccess soa(Thread::Current());
-        ArtMethod* art_method =
-            class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
-                invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
-        return art_method != nullptr &&
-            (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
+        return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
       }
       return false;
 
@@ -563,8 +131,8 @@
   }
 }
 
-// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
 void IntrinsicsRecognizer::Run() {
+  ScopedObjectAccess soa(Thread::Current());
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
@@ -572,26 +140,20 @@
       HInstruction* inst = inst_it.Current();
       if (inst->IsInvoke()) {
         HInvoke* invoke = inst->AsInvoke();
-        InlineMethod method;
-        const DexFile& dex_file = invoke->GetDexFile();
-        DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
-        DCHECK(inliner != nullptr);
-        if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
-          Intrinsics intrinsic = GetIntrinsic(method);
-
-          if (intrinsic != Intrinsics::kNone) {
-            if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
-              LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
-                  << intrinsic << " for "
-                  << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
-                  << invoke->DebugName();
-            } else {
-              invoke->SetIntrinsic(intrinsic,
-                                   NeedsEnvironmentOrCache(intrinsic),
-                                   GetSideEffects(intrinsic),
-                                   GetExceptions(intrinsic));
-              MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
-            }
+        ArtMethod* art_method = invoke->GetResolvedMethod();
+        if (art_method != nullptr && art_method->IsIntrinsic()) {
+          Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
+          if (!CheckInvokeType(intrinsic, invoke)) {
+            LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
+                << intrinsic << " for "
+                << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
+                << invoke->DebugName();
+          } else {
+            invoke->SetIntrinsic(intrinsic,
+                                 NeedsEnvironmentOrCache(intrinsic),
+                                 GetSideEffects(intrinsic),
+                                 GetExceptions(intrinsic));
+            MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
           }
         }
       }
@@ -604,7 +166,7 @@
     case Intrinsics::kNone:
       os << "None";
       break;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     case Intrinsics::k ## Name: \
       os << # Name; \
       break;
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 62f731d..1e73cf6 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -34,17 +34,14 @@
 // Recognize intrinsics from HInvoke nodes.
 class IntrinsicsRecognizer : public HOptimization {
  public:
-  IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver, OptimizingCompilerStats* stats)
-      : HOptimization(graph, kIntrinsicsRecognizerPassName, stats),
-        driver_(driver) {}
+  IntrinsicsRecognizer(HGraph* graph, OptimizingCompilerStats* stats)
+      : HOptimization(graph, kIntrinsicsRecognizerPassName, stats) {}
 
   void Run() OVERRIDE;
 
   static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition";
 
  private:
-  CompilerDriver* driver_;
-
   DISALLOW_COPY_AND_ASSIGN(IntrinsicsRecognizer);
 };
 
@@ -58,7 +55,7 @@
     switch (invoke->GetIntrinsic()) {
       case Intrinsics::kNone:
         return;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, ...) \
       case Intrinsics::k ## Name: \
         Visit ## Name(invoke);    \
         return;
@@ -73,7 +70,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironment, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, ...) \
   virtual void Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
   }
 #include "intrinsics_list.h"
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 67640a1..96a6ecb 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -657,7 +657,7 @@
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
   if (can_call && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
@@ -1039,6 +1039,11 @@
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
+  // Need temporary registers for String compression's feature.
+  if (mirror::kUseStringCompression) {
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
@@ -1053,10 +1058,16 @@
   Register temp0 = locations->GetTemp(0).AsRegister<Register>();
   Register temp1 = locations->GetTemp(1).AsRegister<Register>();
   Register temp2 = locations->GetTemp(2).AsRegister<Register>();
+  Register temp3, temp4;
+  if (mirror::kUseStringCompression) {
+    temp3 = locations->GetTemp(3).AsRegister<Register>();
+    temp4 = locations->GetTemp(4).AsRegister<Register>();
+  }
 
   Label loop;
   Label find_char_diff;
   Label end;
+  Label different_compression;
 
   // Get offsets of count and value fields within a string object.
   const int32_t count_offset = mirror::String::CountOffset().Int32Value();
@@ -1077,20 +1088,40 @@
   // Reference equality check, return 0 if same reference.
   __ subs(out, str, ShifterOperand(arg));
   __ b(&end, EQ);
-  // Load lengths of this and argument strings.
-  __ ldr(temp2, Address(str, count_offset));
-  __ ldr(temp1, Address(arg, count_offset));
+  if (mirror::kUseStringCompression) {
+    // Load lengths of this and argument strings.
+    __ ldr(temp3, Address(str, count_offset));
+    __ ldr(temp4, Address(arg, count_offset));
+    // Clean out compression flag from lengths.
+    __ bic(temp0, temp3, ShifterOperand(0x80000000));
+    __ bic(IP, temp4, ShifterOperand(0x80000000));
+  } else {
+    // Load lengths of this and argument strings.
+    __ ldr(temp0, Address(str, count_offset));
+    __ ldr(IP, Address(arg, count_offset));
+  }
   // out = length diff.
-  __ subs(out, temp2, ShifterOperand(temp1));
+  __ subs(out, temp0, ShifterOperand(IP));
   // temp0 = min(len(str), len(arg)).
-  __ it(Condition::LT, kItElse);
-  __ mov(temp0, ShifterOperand(temp2), Condition::LT);
-  __ mov(temp0, ShifterOperand(temp1), Condition::GE);
+  __ it(GT);
+  __ mov(temp0, ShifterOperand(IP), GT);
   // Shorter string is empty?
   __ CompareAndBranchIfZero(temp0, &end);
 
+  if (mirror::kUseStringCompression) {
+    // Check if both strings using same compression style to use this comparison loop.
+    __ eors(temp3, temp3, ShifterOperand(temp4));
+    __ b(&different_compression, MI);
+  }
   // Store offset of string value in preparation for comparison loop.
   __ mov(temp1, ShifterOperand(value_offset));
+  if (mirror::kUseStringCompression) {
+    // For string compression, calculate the number of bytes to compare (not chars).
+    // This could in theory exceed INT32_MAX, so treat temp0 as unsigned.
+    __ cmp(temp4, ShifterOperand(0));
+    __ it(GE);
+    __ add(temp0, temp0, ShifterOperand(temp0), GE);
+  }
 
   // Assertions that must hold in order to compare multiple characters at a time.
   CHECK_ALIGNED(value_offset, 8);
@@ -1100,6 +1131,7 @@
   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
   DCHECK_EQ(char_size, 2u);
 
+  Label find_char_diff_2nd_cmp;
   // Unrolled loop comparing 4x16-bit chars per iteration (ok because of string data alignment).
   __ Bind(&loop);
   __ ldr(IP, Address(str, temp1));
@@ -1107,43 +1139,113 @@
   __ cmp(IP, ShifterOperand(temp2));
   __ b(&find_char_diff, NE);
   __ add(temp1, temp1, ShifterOperand(char_size * 2));
-  __ sub(temp0, temp0, ShifterOperand(2));
 
   __ ldr(IP, Address(str, temp1));
   __ ldr(temp2, Address(arg, temp1));
   __ cmp(IP, ShifterOperand(temp2));
-  __ b(&find_char_diff, NE);
+  __ b(&find_char_diff_2nd_cmp, NE);
   __ add(temp1, temp1, ShifterOperand(char_size * 2));
-  __ subs(temp0, temp0, ShifterOperand(2));
-
-  __ b(&loop, GT);
+  // With string compression, we have compared 8 bytes, otherwise 4 chars.
+  __ subs(temp0, temp0, ShifterOperand(mirror::kUseStringCompression ? 8 : 4));
+  __ b(&loop, HI);
   __ b(&end);
 
-  // Find the single 16-bit character difference.
+  __ Bind(&find_char_diff_2nd_cmp);
+  if (mirror::kUseStringCompression) {
+    __ subs(temp0, temp0, ShifterOperand(4));  // 4 bytes previously compared.
+    __ b(&end, LS);  // Was the second comparison fully beyond the end?
+  } else {
+    // Without string compression, we can start treating temp0 as signed
+    // and rely on the signed comparison below.
+    __ sub(temp0, temp0, ShifterOperand(2));
+  }
+
+  // Find the single character difference.
   __ Bind(&find_char_diff);
   // Get the bit position of the first character that differs.
   __ eor(temp1, temp2, ShifterOperand(IP));
   __ rbit(temp1, temp1);
   __ clz(temp1, temp1);
 
-  // temp0 = number of 16-bit characters remaining to compare.
-  // (it could be < 1 if a difference is found after the first SUB in the comparison loop, and
-  // after the end of the shorter string data).
+  // temp0 = number of characters remaining to compare.
+  // (Without string compression, it could be < 1 if a difference is found by the second CMP
+  // in the comparison loop, and after the end of the shorter string data).
 
-  // (temp1 >> 4) = character where difference occurs between the last two words compared, on the
-  // interval [0,1] (0 for low half-word different, 1 for high half-word different).
+  // Without string compression (temp1 >> 4) = character where difference occurs between the last
+  // two words compared, in the interval [0,1].
+  // (0 for low half-word different, 1 for high half-word different).
+  // With string compression, (temp1 << 3) = byte where the difference occurs,
+  // in the interval [0,3].
 
-  // If temp0 <= (temp1 >> 4), the difference occurs outside the remaining string data, so just
-  // return length diff (out).
-  __ cmp(temp0, ShifterOperand(temp1, LSR, 4));
-  __ b(&end, LE);
+  // If temp0 <= (temp1 >> (kUseStringCompression ? 3 : 4)), the difference occurs outside
+  // the remaining string data, so just return length diff (out).
+  // The comparison is unsigned for string compression, otherwise signed.
+  __ cmp(temp0, ShifterOperand(temp1, LSR, mirror::kUseStringCompression ? 3 : 4));
+  __ b(&end, mirror::kUseStringCompression ? LS : LE);
   // Extract the characters and calculate the difference.
+  Label uncompressed_string, continue_process;
+  if (mirror::kUseStringCompression) {
+    __ cmp(temp4, ShifterOperand(0));
+    __ b(&uncompressed_string, GE);
+    __ bic(temp1, temp1, ShifterOperand(0x7));
+    __ b(&continue_process);
+  }
+  __ Bind(&uncompressed_string);
   __ bic(temp1, temp1, ShifterOperand(0xf));
+  __ Bind(&continue_process);
+
   __ Lsr(temp2, temp2, temp1);
   __ Lsr(IP, IP, temp1);
+  Label calculate_difference, uncompressed_string_extract_chars;
+  if (mirror::kUseStringCompression) {
+    __ cmp(temp4, ShifterOperand(0));
+    __ b(&uncompressed_string_extract_chars, GE);
+    __ ubfx(temp2, temp2, 0, 8);
+    __ ubfx(IP, IP, 0, 8);
+    __ b(&calculate_difference);
+  }
+  __ Bind(&uncompressed_string_extract_chars);
   __ movt(temp2, 0);
   __ movt(IP, 0);
+  __ Bind(&calculate_difference);
   __ sub(out, IP, ShifterOperand(temp2));
+  __ b(&end);
+
+  if (mirror::kUseStringCompression) {
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    Label loop_arg_compressed, loop_this_compressed, find_diff;
+    // Comparison for different compression style.
+    // This part is when THIS is compressed and ARG is not.
+    __ Bind(&different_compression);
+    __ add(temp2, str, ShifterOperand(value_offset));
+    __ add(temp3, arg, ShifterOperand(value_offset));
+    __ cmp(temp4, ShifterOperand(0));
+    __ b(&loop_arg_compressed, LT);
+
+    __ Bind(&loop_this_compressed);
+    __ ldrb(IP, Address(temp2, c_char_size, Address::PostIndex));
+    __ ldrh(temp4, Address(temp3, char_size, Address::PostIndex));
+    __ cmp(IP, ShifterOperand(temp4));
+    __ b(&find_diff, NE);
+    __ subs(temp0, temp0, ShifterOperand(1));
+    __ b(&loop_this_compressed, GT);
+    __ b(&end);
+
+    // This part is when THIS is not compressed and ARG is.
+    __ Bind(&loop_arg_compressed);
+    __ ldrh(IP, Address(temp2, char_size, Address::PostIndex));
+    __ ldrb(temp4, Address(temp3, c_char_size, Address::PostIndex));
+    __ cmp(IP, ShifterOperand(temp4));
+    __ b(&find_diff, NE);
+    __ subs(temp0, temp0, ShifterOperand(1));
+    __ b(&loop_arg_compressed, GT);
+    __ b(&end);
+
+    // Calculate the difference.
+    __ Bind(&find_diff);
+    __ sub(out, IP, ShifterOperand(temp4));
+  }
 
   __ Bind(&end);
 
@@ -1180,7 +1282,7 @@
   Register temp1 = locations->GetTemp(1).AsRegister<Register>();
   Register temp2 = locations->GetTemp(2).AsRegister<Register>();
 
-  Label loop;
+  Label loop, preloop;
   Label end;
   Label return_true;
   Label return_false;
@@ -1214,11 +1316,15 @@
   __ ldr(temp, Address(str, count_offset));
   __ ldr(temp1, Address(arg, count_offset));
   // Check if lengths are equal, return false if they're not.
+  // Also compares the compression style, if differs return false.
   __ cmp(temp, ShifterOperand(temp1));
   __ b(&return_false, NE);
   // Return true if both strings are empty.
+  if (mirror::kUseStringCompression) {
+    // Length needs to be masked out first because 0 is treated as compressed.
+    __ bic(temp, temp, ShifterOperand(0x80000000));
+  }
   __ cbz(temp, &return_true);
-
   // Reference equality check, return true if same reference.
   __ cmp(str, ShifterOperand(arg));
   __ b(&return_true, EQ);
@@ -1227,10 +1333,19 @@
   DCHECK_ALIGNED(value_offset, 4);
   static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
 
-  __ LoadImmediate(temp1, value_offset);
-
+  if (mirror::kUseStringCompression) {
+    // If not compressed, directly to fast compare. Else do preprocess on length.
+    __ cmp(temp1, ShifterOperand(0));
+    __ b(&preloop, GT);
+    // Mask out compression flag and adjust length for compressed string (8-bit)
+    // as if it is a 16-bit data, new_length = (length + 1) / 2.
+    __ add(temp, temp, ShifterOperand(1));
+    __ Lsr(temp, temp, 1);
+    __ Bind(&preloop);
+  }
   // Loop to compare strings 2 characters at a time starting at the front of the string.
   // Ok to do this because strings with an odd length are zero-padded.
+  __ LoadImmediate(temp1, value_offset);
   __ Bind(&loop);
   __ ldr(out, Address(str, temp1));
   __ ldr(temp2, Address(arg, temp1));
@@ -2330,22 +2445,31 @@
   Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
   Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
 
-  // src range to copy.
-  __ add(src_ptr, srcObj, ShifterOperand(value_offset));
-  __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
-
+  Label done, compressed_string_loop;
   // dst to be copied.
   __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
   __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
 
   __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
-
-  // Do the copy.
-  Label loop, remainder, done;
-
   // Early out for valid zero-length retrievals.
   __ b(&done, EQ);
 
+  // src range to copy.
+  __ add(src_ptr, srcObj, ShifterOperand(value_offset));
+  Label compressed_string_preloop;
+  if (mirror::kUseStringCompression) {
+    // Location of count in string.
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+    // String's length.
+    __ ldr(IP, Address(srcObj, count_offset));
+    __ cmp(IP, ShifterOperand(0));
+    __ b(&compressed_string_preloop, LT);
+  }
+  __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
+
+  // Do the copy.
+  Label loop, remainder;
+
   // Save repairing the value of num_chr on the < 4 character path.
   __ subs(IP, num_chr, ShifterOperand(4));
   __ b(&remainder, LT);
@@ -2374,6 +2498,20 @@
   __ subs(num_chr, num_chr, ShifterOperand(1));
   __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
   __ b(&remainder, GT);
+  __ b(&done);
+
+  if (mirror::kUseStringCompression) {
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+    __ Bind(&compressed_string_preloop);
+    __ add(src_ptr, src_ptr, ShifterOperand(srcBegin));
+    __ Bind(&compressed_string_loop);
+    __ ldrb(IP, Address(src_ptr, c_char_size, Address::PostIndex));
+    __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
+    __ subs(num_chr, num_chr, ShifterOperand(1));
+    __ b(&compressed_string_loop, GT);
+  }
 
   __ Bind(&done);
 }
diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h
index c671700..7f20ea4 100644
--- a/compiler/optimizing/intrinsics_arm.h
+++ b/compiler/optimizing/intrinsics_arm.h
@@ -37,7 +37,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -64,7 +64,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 082076d..e2c1802 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -895,7 +895,7 @@
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
   if (can_call && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
@@ -1223,6 +1223,11 @@
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
+  // Need temporary registers for String compression's feature.
+  if (mirror::kUseStringCompression) {
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
@@ -1239,10 +1244,16 @@
   Register temp0 = WRegisterFrom(locations->GetTemp(0));
   Register temp1 = WRegisterFrom(locations->GetTemp(1));
   Register temp2 = WRegisterFrom(locations->GetTemp(2));
+  Register temp3, temp5;
+  if (mirror::kUseStringCompression) {
+    temp3 = WRegisterFrom(locations->GetTemp(3));
+    temp5 = WRegisterFrom(locations->GetTemp(4));
+  }
 
   vixl::aarch64::Label loop;
   vixl::aarch64::Label find_char_diff;
   vixl::aarch64::Label end;
+  vixl::aarch64::Label different_compression;
 
   // Get offsets of count and value fields within a string object.
   const int32_t count_offset = mirror::String::CountOffset().Int32Value();
@@ -1263,9 +1274,18 @@
   // Reference equality check, return 0 if same reference.
   __ Subs(out, str, arg);
   __ B(&end, eq);
-  // Load lengths of this and argument strings.
-  __ Ldr(temp0, HeapOperand(str, count_offset));
-  __ Ldr(temp1, HeapOperand(arg, count_offset));
+  if (mirror::kUseStringCompression) {
+    // Load lengths of this and argument strings.
+    __ Ldr(temp3, HeapOperand(str, count_offset));
+    __ Ldr(temp5, HeapOperand(arg, count_offset));
+    // Clean out compression flag from lengths.
+    __ Bic(temp0, temp3, Operand(static_cast<int32_t>(0x80000000)));
+    __ Bic(temp1, temp5, Operand(static_cast<int32_t>(0x80000000)));
+  } else {
+    // Load lengths of this and argument strings.
+    __ Ldr(temp0, HeapOperand(str, count_offset));
+    __ Ldr(temp1, HeapOperand(arg, count_offset));
+  }
   // Return zero if both strings are empty.
   __ Orr(out, temp0, temp1);
   __ Cbz(out, &end);
@@ -1276,8 +1296,22 @@
   // Shorter string is empty?
   __ Cbz(temp2, &end);
 
+  if (mirror::kUseStringCompression) {
+    // Check if both strings using same compression style to use this comparison loop.
+    __ Eor(temp3.W(), temp3, Operand(temp5));
+    __ Tbnz(temp3.W(), kWRegSize - 1, &different_compression);
+  }
   // Store offset of string value in preparation for comparison loop.
   __ Mov(temp1, value_offset);
+  if (mirror::kUseStringCompression) {
+    // For string compression, calculate the number of bytes to compare (not chars).
+    // This could be in theory exceed INT32_MAX, so treat temp2 as unsigned.
+    vixl::aarch64::Label let_it_signed;
+    __ Cmp(temp5, Operand(0));
+    __ B(lt, &let_it_signed);
+    __ Add(temp2, temp2, Operand(temp2));
+    __ Bind(&let_it_signed);
+  }
 
   UseScratchRegisterScope scratch_scope(masm);
   Register temp4 = scratch_scope.AcquireX();
@@ -1299,29 +1333,90 @@
   __ Cmp(temp4, temp0);
   __ B(ne, &find_char_diff);
   __ Add(temp1, temp1, char_size * 4);
-  __ Subs(temp2, temp2, 4);
-  __ B(gt, &loop);
+  // With string compression, we have compared 8 bytes, otherwise 4 chars.
+  __ Subs(temp2, temp2, (mirror::kUseStringCompression) ? 8 : 4);
+  __ B(hi, &loop);
   __ B(&end);
 
   // Promote temp1 to an X reg, ready for EOR.
   temp1 = temp1.X();
 
-  // Find the single 16-bit character difference.
+  // Find the single character difference.
   __ Bind(&find_char_diff);
   // Get the bit position of the first character that differs.
   __ Eor(temp1, temp0, temp4);
   __ Rbit(temp1, temp1);
   __ Clz(temp1, temp1);
-  // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
+  // If the number of chars remaining <= the index where the difference occurs (0-3), then
   // the difference occurs outside the remaining string data, so just return length diff (out).
-  __ Cmp(temp2, Operand(temp1.W(), LSR, 4));
-  __ B(le, &end);
+  // Unlike ARM, we're doing the comparison in one go here, without the subtraction at the
+  // find_char_diff_2nd_cmp path, so it doesn't matter whether the comparison is signed or
+  // unsigned when string compression is disabled.
+  // When it's enabled, the comparison must be unsigned.
+  __ Cmp(temp2, Operand(temp1.W(), LSR, (mirror::kUseStringCompression) ? 3 : 4));
+  __ B(ls, &end);
   // Extract the characters and calculate the difference.
+  vixl::aarch64::Label uncompressed_string, continue_process;
+  if (mirror:: kUseStringCompression) {
+    __ Tbz(temp5, kWRegSize - 1, &uncompressed_string);
+    __ Bic(temp1, temp1, 0x7);
+    __ B(&continue_process);
+  }
+  __ Bind(&uncompressed_string);
   __ Bic(temp1, temp1, 0xf);
+  __ Bind(&continue_process);
+
   __ Lsr(temp0, temp0, temp1);
   __ Lsr(temp4, temp4, temp1);
+  vixl::aarch64::Label uncompressed_string_extract_chars;
+  if (mirror::kUseStringCompression) {
+    __ Tbz(temp5, kWRegSize - 1, &uncompressed_string_extract_chars);
+    __ And(temp4, temp4, 0xff);
+    __ Sub(out, temp4.W(), Operand(temp0.W(), UXTB));
+    __ B(&end);
+  }
+  __ Bind(&uncompressed_string_extract_chars);
   __ And(temp4, temp4, 0xffff);
   __ Sub(out, temp4.W(), Operand(temp0.W(), UXTH));
+  __ B(&end);
+
+  if (mirror::kUseStringCompression) {
+    vixl::aarch64::Label loop_this_compressed, loop_arg_compressed, find_diff;
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    temp0 = temp0.W();
+    temp1 = temp1.W();
+    // Comparison for different compression style.
+    // This part is when THIS is compressed and ARG is not.
+    __ Bind(&different_compression);
+    __ Add(temp0, str, Operand(value_offset));
+    __ Add(temp1, arg, Operand(value_offset));
+    __ Cmp(temp5, Operand(0));
+    __ B(lt, &loop_arg_compressed);
+
+    __ Bind(&loop_this_compressed);
+    __ Ldrb(temp3, MemOperand(temp0.X(), c_char_size, PostIndex));
+    __ Ldrh(temp5, MemOperand(temp1.X(), char_size, PostIndex));
+    __ Cmp(temp3, Operand(temp5));
+    __ B(ne, &find_diff);
+    __ Subs(temp2, temp2, 1);
+    __ B(gt, &loop_this_compressed);
+    __ B(&end);
+
+    // This part is when THIS is not compressed and ARG is.
+    __ Bind(&loop_arg_compressed);
+    __ Ldrh(temp3, MemOperand(temp0.X(), char_size, PostIndex));
+    __ Ldrb(temp5, MemOperand(temp1.X(), c_char_size, PostIndex));
+    __ Cmp(temp3, Operand(temp5));
+    __ B(ne, &find_diff);
+    __ Subs(temp2, temp2, 1);
+    __ B(gt, &loop_arg_compressed);
+    __ B(&end);
+
+    // Calculate the difference.
+    __ Bind(&find_diff);
+    __ Sub(out, temp3.W(), Operand(temp5.W(), UXTH));
+  }
 
   __ Bind(&end);
 
@@ -1356,7 +1451,7 @@
   Register temp1 = WRegisterFrom(locations->GetTemp(0));
   Register temp2 = WRegisterFrom(locations->GetTemp(1));
 
-  vixl::aarch64::Label loop;
+  vixl::aarch64::Label loop, preloop;
   vixl::aarch64::Label end;
   vixl::aarch64::Label return_true;
   vixl::aarch64::Label return_false;
@@ -1394,22 +1489,37 @@
   __ Ldr(temp, MemOperand(str.X(), count_offset));
   __ Ldr(temp1, MemOperand(arg.X(), count_offset));
   // Check if lengths are equal, return false if they're not.
+  // Also compares the compression style, if differs return false.
   __ Cmp(temp, temp1);
   __ B(&return_false, ne);
-  // Store offset of string value in preparation for comparison loop
-  __ Mov(temp1, value_offset);
   // Return true if both strings are empty.
+  if (mirror::kUseStringCompression) {
+    // Length needs to be masked out first because 0 is treated as compressed.
+    __ Bic(temp, temp, Operand(static_cast<int32_t>(0x80000000)));
+  }
   __ Cbz(temp, &return_true);
 
   // Assertions that must hold in order to compare strings 4 characters at a time.
   DCHECK_ALIGNED(value_offset, 8);
   static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
 
+  if (mirror::kUseStringCompression) {
+    // If not compressed, directly to fast compare. Else do preprocess on length.
+    __ Cmp(temp1, Operand(0));
+    __ B(&preloop, gt);
+    // Mask out compression flag and adjust length for compressed string (8-bit)
+    // as if it is a 16-bit data, new_length = (length + 1) / 2
+    __ Add(temp, temp, 1);
+    __ Lsr(temp, temp, 1);
+  }
+
   temp1 = temp1.X();
   temp2 = temp2.X();
-
   // Loop to compare strings 4 characters at a time starting at the beginning of the string.
   // Ok to do this because strings are zero-padded to be 8-byte aligned.
+  // Store offset of string value in preparation for comparison loop
+  __ Bind(&preloop);
+  __ Mov(temp1, value_offset);
   __ Bind(&loop);
   __ Ldr(out, MemOperand(str.X(), temp1));
   __ Ldr(temp2, MemOperand(arg.X(), temp1));
@@ -1773,6 +1883,10 @@
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
   locations->AddTemp(Location::RequiresRegister());
+  // Need temporary register for String compression feature.
+  if (mirror::kUseStringCompression) {
+    locations->AddTemp(Location::RequiresRegister());
+  }
 }
 
 void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
@@ -1800,29 +1914,41 @@
   Register src_ptr = XRegisterFrom(locations->GetTemp(0));
   Register num_chr = XRegisterFrom(locations->GetTemp(1));
   Register tmp1 = XRegisterFrom(locations->GetTemp(2));
+  Register tmp3;
+  if (mirror::kUseStringCompression) {
+    tmp3 = WRegisterFrom(locations->GetTemp(3));
+  }
 
   UseScratchRegisterScope temps(masm);
   Register dst_ptr = temps.AcquireX();
   Register tmp2 = temps.AcquireX();
 
-  // src address to copy from.
-  __ Add(src_ptr, srcObj, Operand(value_offset));
-  __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
+  vixl::aarch64::Label done;
+  vixl::aarch64::Label compressed_string_loop;
+  __ Sub(num_chr, srcEnd, srcBegin);
+  // Early out for valid zero-length retrievals.
+  __ Cbz(num_chr, &done);
 
   // dst address start to copy to.
   __ Add(dst_ptr, dstObj, Operand(data_offset));
   __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
 
-  __ Sub(num_chr, srcEnd, srcBegin);
+  // src address to copy from.
+  __ Add(src_ptr, srcObj, Operand(value_offset));
+  vixl::aarch64::Label compressed_string_preloop;
+  if (mirror::kUseStringCompression) {
+    // Location of count in string.
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+    // String's length.
+    __ Ldr(tmp3, MemOperand(srcObj, count_offset));
+    __ Tbnz(tmp3, kWRegSize - 1, &compressed_string_preloop);
+  }
+  __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
 
   // Do the copy.
   vixl::aarch64::Label loop;
-  vixl::aarch64::Label done;
   vixl::aarch64::Label remainder;
 
-  // Early out for valid zero-length retrievals.
-  __ Cbz(num_chr, &done);
-
   // Save repairing the value of num_chr on the < 8 character path.
   __ Subs(tmp1, num_chr, 8);
   __ B(lt, &remainder);
@@ -1848,6 +1974,20 @@
   __ Subs(num_chr, num_chr, 1);
   __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
   __ B(gt, &remainder);
+  __ B(&done);
+
+  if (mirror::kUseStringCompression) {
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    __ Bind(&compressed_string_preloop);
+    __ Add(src_ptr, src_ptr, Operand(srcBegin));
+    // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+    __ Bind(&compressed_string_loop);
+    __ Ldrb(tmp1, MemOperand(src_ptr, c_char_size, PostIndex));
+    __ Strh(tmp1, MemOperand(dst_ptr, char_size, PostIndex));
+    __ Subs(num_chr, num_chr, Operand(1));
+    __ B(gt, &compressed_string_loop);
+  }
 
   __ Bind(&done);
 }
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
index 5251536..28e41cb 100644
--- a/compiler/optimizing/intrinsics_arm64.h
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -42,7 +42,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -66,7 +66,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
deleted file mode 100644
index db60238..0000000
--- a/compiler/optimizing/intrinsics_list.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
-#define ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
-
-// All intrinsics supported by the optimizing compiler. Format is name, then whether it is expected
-// to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual), then whether it requires an
-// environment, may have side effects, or may throw exceptions.
-
-#define INTRINSICS_LIST(V) \
-  V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(IntegerSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(LongSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAbsLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAbsInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMinDoubleDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMinFloatFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMinLongLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMinIntInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMaxDoubleDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMaxFloatFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMaxLongLong, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathMaxIntInt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathCos, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathSin, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAcos, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAsin, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAtan, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathAtan2, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathCbrt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathCosh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathExp, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathExpm1, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathHypot, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathLog, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathLog10, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathNextAfter, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathSinh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathTan, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathTanh, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathSqrt, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathCeil, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathFloor, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathRint, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathRoundDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MathRoundFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(SystemArrayCopyChar, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(SystemArrayCopy, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(ThreadCurrentThread, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
-  V(MemoryPeekByte, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(MemoryPeekIntNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(MemoryPeekLongNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(MemoryPeekShortNative, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(MemoryPokeByte, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow) \
-  V(MemoryPokeIntNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow) \
-  V(MemoryPokeLongNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow) \
-  V(MemoryPokeShortNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow) \
-  V(StringCharAt, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringCompareTo, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringEquals, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringGetCharsNoCheck, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringIndexOf, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringIndexOfAfter, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \
-  V(StringIsEmpty, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \
-  V(StringLength, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \
-  V(StringNewStringFromBytes, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(StringNewStringFromChars, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(StringNewStringFromString, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeCASInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeCASLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeCASObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGet, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetObjectVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePut, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutObjectOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutObjectVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \
-  V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow)
-
-#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
-#undef ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_   // #define is only for lint.
diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h
index 575a7d0..e134cb8 100644
--- a/compiler/optimizing/intrinsics_mips.h
+++ b/compiler/optimizing/intrinsics_mips.h
@@ -36,7 +36,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -60,7 +60,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h
index 4137fbd..5b95c26 100644
--- a/compiler/optimizing/intrinsics_mips64.h
+++ b/compiler/optimizing/intrinsics_mips64.h
@@ -36,7 +36,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -60,7 +60,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index d17f85e..f41e4d9 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1401,23 +1401,39 @@
   __ cmpl(str, arg);
   __ j(kEqual, &return_true);
 
-  // Load length of receiver string.
+  // Load length and compression flag of receiver string.
   __ movl(ecx, Address(str, count_offset));
-  // Check if lengths are equal, return false if they're not.
+  // Check if lengths and compression flags are equal, return false if they're not.
+  // Two identical strings will always have same compression style since
+  // compression style is decided on alloc.
   __ cmpl(ecx, Address(arg, count_offset));
   __ j(kNotEqual, &return_false);
-  // Return true if both strings are empty.
-  __ jecxz(&return_true);
 
+  if (mirror::kUseStringCompression) {
+    NearLabel string_uncompressed;
+    // Differ cases into both compressed or both uncompressed. Different compression style
+    // is cut above.
+    __ cmpl(ecx, Immediate(0));
+    __ j(kGreaterEqual, &string_uncompressed);
+    // Divide string length by 2, rounding up, and continue as if uncompressed.
+    // Merge clearing the compression flag (+0x80000000) with +1 for rounding.
+    __ addl(ecx, Immediate(0x80000001));
+    __ shrl(ecx, Immediate(1));
+    __ Bind(&string_uncompressed);
+  }
+  // Return true if strings are empty.
+  __ jecxz(&return_true);
   // Load starting addresses of string values into ESI/EDI as required for repe_cmpsl instruction.
   __ leal(esi, Address(str, value_offset));
   __ leal(edi, Address(arg, value_offset));
 
-  // Divide string length by 2 to compare characters 2 at a time and adjust for odd lengths.
+  // Divide string length by 2 to compare characters 2 at a time and adjust for lengths not
+  // divisible by 2.
   __ addl(ecx, Immediate(1));
   __ shrl(ecx, Immediate(1));
 
-  // Assertions that must hold in order to compare strings 2 characters at a time.
+  // Assertions that must hold in order to compare strings 2 characters (uncompressed)
+  // or 4 characters (compressed) at a time.
   DCHECK_ALIGNED(value_offset, 4);
   static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
 
@@ -1461,6 +1477,10 @@
   locations->AddTemp(Location::RegisterLocation(ECX));
   // Need another temporary to be able to compute the result.
   locations->AddTemp(Location::RequiresRegister());
+  if (mirror::kUseStringCompression) {
+    // Need another temporary to be able to save unflagged string length.
+    locations->AddTemp(Location::RequiresRegister());
+  }
 }
 
 static void GenerateStringIndexOf(HInvoke* invoke,
@@ -1478,6 +1498,8 @@
   Register counter = locations->GetTemp(0).AsRegister<Register>();
   Register string_length = locations->GetTemp(1).AsRegister<Register>();
   Register out = locations->Out().AsRegister<Register>();
+  // Only used when string compression feature is on.
+  Register string_length_flagged;
 
   // Check our assumptions for registers.
   DCHECK_EQ(string_obj, EDI);
@@ -1515,6 +1537,12 @@
 
   // Load string length, i.e., the count field of the string.
   __ movl(string_length, Address(string_obj, count_offset));
+  if (mirror::kUseStringCompression) {
+    string_length_flagged = locations->GetTemp(2).AsRegister<Register>();
+    __ movl(string_length_flagged, string_length);
+    // Mask out first bit used as compression flag.
+    __ andl(string_length, Immediate(INT32_MAX));
+  }
 
   // Do a zero-length check.
   // TODO: Support jecxz.
@@ -1540,20 +1568,50 @@
     __ cmpl(start_index, Immediate(0));
     __ cmovl(kGreater, counter, start_index);
 
-    // Move to the start of the string: string_obj + value_offset + 2 * start_index.
-    __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
+    if (mirror::kUseStringCompression) {
+      NearLabel modify_counter, offset_uncompressed_label;
+      __ cmpl(string_length_flagged, Immediate(0));
+      __ j(kGreaterEqual, &offset_uncompressed_label);
+      // Move to the start of the string: string_obj + value_offset + start_index.
+      __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_1, value_offset));
+      __ jmp(&modify_counter);
 
-    // Now update ecx (the repne scasw work counter). We have string.length - start_index left to
-    // compare.
+      // Move to the start of the string: string_obj + value_offset + 2 * start_index.
+      __ Bind(&offset_uncompressed_label);
+      __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
+
+      // Now update ecx (the repne scasw work counter). We have string.length - start_index left to
+      // compare.
+      __ Bind(&modify_counter);
+    } else {
+      __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
+    }
     __ negl(counter);
     __ leal(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0));
   }
 
-  // Everything is set up for repne scasw:
-  //   * Comparison address in EDI.
-  //   * Counter in ECX.
-  __ repne_scasw();
+  if (mirror::kUseStringCompression) {
+    NearLabel uncompressed_string_comparison;
+    NearLabel comparison_done;
+    __ cmpl(string_length_flagged, Immediate(0));
+    __ j(kGreater, &uncompressed_string_comparison);
 
+    // Check if EAX (search_value) is ASCII.
+    __ cmpl(search_value, Immediate(127));
+    __ j(kGreater, &not_found_label);
+    // Comparing byte-per-byte.
+    __ repne_scasb();
+    __ jmp(&comparison_done);
+
+    // Everything is set up for repne scasw:
+    //   * Comparison address in EDI.
+    //   * Counter in ECX.
+    __ Bind(&uncompressed_string_comparison);
+    __ repne_scasw();
+    __ Bind(&comparison_done);
+  } else {
+    __ repne_scasw();
+  }
   // Did we find a match?
   __ j(kNotEqual, &not_found_label);
 
@@ -1706,38 +1764,64 @@
   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
   DCHECK_EQ(char_size, 2u);
 
-  // Compute the address of the destination buffer.
-  __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
-
-  // Compute the address of the source string.
-  if (srcBegin.IsConstant()) {
-    // Compute the address of the source string by adding the number of chars from
-    // the source beginning to the value offset of a string.
-    __ leal(ESI, Address(obj, srcBegin_value * char_size + value_offset));
-  } else {
-    __ leal(ESI, Address(obj, srcBegin.AsRegister<Register>(),
-                         ScaleFactor::TIMES_2, value_offset));
-  }
-
   // Compute the number of chars (words) to move.
-  // Now is the time to save ECX, since we don't know if it will be used later.
+  // Save ECX, since we don't know if it will be used later.
   __ pushl(ECX);
   int stack_adjust = kX86WordSize;
   __ cfi().AdjustCFAOffset(stack_adjust);
   DCHECK_EQ(srcEnd, ECX);
   if (srcBegin.IsConstant()) {
-    if (srcBegin_value != 0) {
-      __ subl(ECX, Immediate(srcBegin_value));
-    }
+    __ subl(ECX, Immediate(srcBegin_value));
   } else {
     DCHECK(srcBegin.IsRegister());
     __ subl(ECX, srcBegin.AsRegister<Register>());
   }
 
-  // Do the move.
+  NearLabel done;
+  if (mirror::kUseStringCompression) {
+    // Location of count in string
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    __ pushl(EAX);
+    __ cfi().AdjustCFAOffset(stack_adjust);
+
+    NearLabel copy_loop, copy_uncompressed;
+    __ cmpl(Address(obj, count_offset), Immediate(0));
+    __ j(kGreaterEqual, &copy_uncompressed);
+    // Compute the address of the source string by adding the number of chars from
+    // the source beginning to the value offset of a string.
+    __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_1, value_offset));
+
+    // Start the loop to copy String's value to Array of Char.
+    __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
+    __ Bind(&copy_loop);
+    __ jecxz(&done);
+    // Use EAX temporary (convert byte from ESI to word).
+    // TODO: Use LODSB/STOSW (not supported by X86Assembler) with AH initialized to 0.
+    __ movzxb(EAX, Address(ESI, 0));
+    __ movw(Address(EDI, 0), EAX);
+    __ leal(EDI, Address(EDI, char_size));
+    __ leal(ESI, Address(ESI, c_char_size));
+    // TODO: Add support for LOOP to X86Assembler.
+    __ subl(ECX, Immediate(1));
+    __ jmp(&copy_loop);
+    __ Bind(&copy_uncompressed);
+  }
+
+  // Do the copy for uncompressed string.
+  // Compute the address of the destination buffer.
+  __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
+  __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_2, value_offset));
   __ rep_movsw();
 
-  // And restore ECX.
+  __ Bind(&done);
+  if (mirror::kUseStringCompression) {
+    // Restore EAX.
+    __ popl(EAX);
+    __ cfi().AdjustCFAOffset(-stack_adjust);
+  }
+  // Restore ECX.
   __ popl(ECX);
   __ cfi().AdjustCFAOffset(-stack_adjust);
 }
@@ -1977,7 +2061,7 @@
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
   if (can_call && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h
index 08bd197..3743cb1 100644
--- a/compiler/optimizing/intrinsics_x86.h
+++ b/compiler/optimizing/intrinsics_x86.h
@@ -36,7 +36,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -61,7 +61,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index f8f30d9..4b0afca 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1568,14 +1568,27 @@
   __ cmpl(str, arg);
   __ j(kEqual, &return_true);
 
-  // Load length of receiver string.
+  // Load length and compression flag of receiver string.
   __ movl(rcx, Address(str, count_offset));
-  // Check if lengths are equal, return false if they're not.
+  // Check if lengths and compressiond flags are equal, return false if they're not.
+  // Two identical strings will always have same compression style since
+  // compression style is decided on alloc.
   __ cmpl(rcx, Address(arg, count_offset));
   __ j(kNotEqual, &return_false);
+
+  if (mirror::kUseStringCompression) {
+    NearLabel string_uncompressed;
+    // Both string are compressed.
+    __ cmpl(rcx, Immediate(0));
+    __ j(kGreaterEqual, &string_uncompressed);
+    // Divide string length by 2, rounding up, and continue as if uncompressed.
+    // Merge clearing the compression flag with +1 for rounding.
+    __ addl(rcx, Immediate(static_cast<int32_t>(0x80000001)));
+    __ shrl(rcx, Immediate(1));
+    __ Bind(&string_uncompressed);
+  }
   // Return true if both strings are empty.
   __ jrcxz(&return_true);
-
   // Load starting addresses of string values into RSI/RDI as required for repe_cmpsq instruction.
   __ leal(rsi, Address(str, value_offset));
   __ leal(rdi, Address(arg, value_offset));
@@ -1584,7 +1597,8 @@
   __ addl(rcx, Immediate(3));
   __ shrl(rcx, Immediate(2));
 
-  // Assertions that must hold in order to compare strings 4 characters at a time.
+  // Assertions that must hold in order to compare strings 4 characters (uncompressed)
+  // or 8 characters (compressed) at a time.
   DCHECK_ALIGNED(value_offset, 8);
   static_assert(IsAligned<8>(kObjectAlignment), "String is not zero padded");
 
@@ -1674,7 +1688,8 @@
     __ j(kAbove, slow_path->GetEntryLabel());
   }
 
-  // From here down, we know that we are looking for a char that fits in 16 bits.
+  // From here down, we know that we are looking for a char that fits in
+  // 16 bits (uncompressed) or 8 bits (compressed).
   // Location of reference to data array within the String object.
   int32_t value_offset = mirror::String::ValueOffset().Int32Value();
   // Location of count within the String object.
@@ -1682,6 +1697,12 @@
 
   // Load string length, i.e., the count field of the string.
   __ movl(string_length, Address(string_obj, count_offset));
+  if (mirror::kUseStringCompression) {
+    // Use TMP to keep string_length_flagged.
+    __ movl(CpuRegister(TMP), string_length);
+    // Mask out first bit used as compression flag.
+    __ andl(string_length, Immediate(INT32_MAX));
+  }
 
   // Do a length check.
   // TODO: Support jecxz.
@@ -1692,7 +1713,6 @@
   if (start_at_zero) {
     // Number of chars to scan is the same as the string length.
     __ movl(counter, string_length);
-
     // Move to the start of the string.
     __ addq(string_obj, Immediate(value_offset));
   } else {
@@ -1707,19 +1727,44 @@
     __ cmpl(start_index, Immediate(0));
     __ cmov(kGreater, counter, start_index, /* is64bit */ false);  // 32-bit copy is enough.
 
-    // Move to the start of the string: string_obj + value_offset + 2 * start_index.
-    __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
-
+    if (mirror::kUseStringCompression) {
+      NearLabel modify_counter, offset_uncompressed_label;
+      __ cmpl(CpuRegister(TMP), Immediate(0));
+      __ j(kGreaterEqual, &offset_uncompressed_label);
+      __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_1, value_offset));
+      __ jmp(&modify_counter);
+      // Move to the start of the string: string_obj + value_offset + 2 * start_index.
+      __ Bind(&offset_uncompressed_label);
+      __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
+      __ Bind(&modify_counter);
+    } else {
+      __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset));
+    }
     // Now update ecx, the work counter: it's gonna be string.length - start_index.
     __ negq(counter);  // Needs to be 64-bit negation, as the address computation is 64-bit.
     __ leaq(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0));
   }
 
-  // Everything is set up for repne scasw:
-  //   * Comparison address in RDI.
-  //   * Counter in ECX.
-  __ repne_scasw();
-
+  if (mirror::kUseStringCompression) {
+    NearLabel uncompressed_string_comparison;
+    NearLabel comparison_done;
+    __ cmpl(CpuRegister(TMP), Immediate(0));
+    __ j(kGreater, &uncompressed_string_comparison);
+    // Check if RAX (search_value) is ASCII.
+    __ cmpl(search_value, Immediate(127));
+    __ j(kGreater, &not_found_label);
+    // Comparing byte-per-byte.
+    __ repne_scasb();
+    __ jmp(&comparison_done);
+    // Everything is set up for repne scasw:
+    //   * Comparison address in RDI.
+    //   * Counter in ECX.
+    __ Bind(&uncompressed_string_comparison);
+    __ repne_scasw();
+    __ Bind(&comparison_done);
+  } else {
+    __ repne_scasw();
+  }
   // Did we find a match?
   __ j(kNotEqual, &not_found_label);
 
@@ -1871,32 +1916,54 @@
   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
   DCHECK_EQ(char_size, 2u);
 
-  // Compute the address of the destination buffer.
-  __ leaq(CpuRegister(RDI), Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
-
-  // Compute the address of the source string.
-  if (srcBegin.IsConstant()) {
-    // Compute the address of the source string by adding the number of chars from
-    // the source beginning to the value offset of a string.
-    __ leaq(CpuRegister(RSI), Address(obj, srcBegin_value * char_size + value_offset));
-  } else {
-    __ leaq(CpuRegister(RSI), Address(obj, srcBegin.AsRegister<CpuRegister>(),
-                                      ScaleFactor::TIMES_2, value_offset));
-  }
-
+  NearLabel done;
   // Compute the number of chars (words) to move.
   __ movl(CpuRegister(RCX), srcEnd);
   if (srcBegin.IsConstant()) {
-    if (srcBegin_value != 0) {
-      __ subl(CpuRegister(RCX), Immediate(srcBegin_value));
-    }
+    __ subl(CpuRegister(RCX), Immediate(srcBegin_value));
   } else {
     DCHECK(srcBegin.IsRegister());
     __ subl(CpuRegister(RCX), srcBegin.AsRegister<CpuRegister>());
   }
+  if (mirror::kUseStringCompression) {
+    NearLabel copy_uncompressed, copy_loop;
+    const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte);
+    DCHECK_EQ(c_char_size, 1u);
+    // Location of count in string.
+    const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
 
+    __ cmpl(Address(obj, count_offset), Immediate(0));
+    __ j(kGreaterEqual, &copy_uncompressed);
+    // Compute the address of the source string by adding the number of chars from
+    // the source beginning to the value offset of a string.
+    __ leaq(CpuRegister(RSI),
+            CodeGeneratorX86_64::ArrayAddress(obj, srcBegin, TIMES_1, value_offset));
+    // Start the loop to copy String's value to Array of Char.
+    __ leaq(CpuRegister(RDI), Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
+
+    __ Bind(&copy_loop);
+    __ jrcxz(&done);
+    // Use TMP as temporary (convert byte from RSI to word).
+    // TODO: Selecting RAX as the temporary and using LODSB/STOSW.
+    __ movzxb(CpuRegister(TMP), Address(CpuRegister(RSI), 0));
+    __ movw(Address(CpuRegister(RDI), 0), CpuRegister(TMP));
+    __ leaq(CpuRegister(RDI), Address(CpuRegister(RDI), char_size));
+    __ leaq(CpuRegister(RSI), Address(CpuRegister(RSI), c_char_size));
+    // TODO: Add support for LOOP to X86_64Assembler.
+    __ subl(CpuRegister(RCX), Immediate(1));
+    __ jmp(&copy_loop);
+
+    __ Bind(&copy_uncompressed);
+  }
+
+  __ leaq(CpuRegister(RSI),
+          CodeGeneratorX86_64::ArrayAddress(obj, srcBegin, TIMES_2, value_offset));
+  // Compute the address of the destination buffer.
+  __ leaq(CpuRegister(RDI), Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset));
   // Do the move.
   __ rep_movsw();
+
+  __ Bind(&done);
 }
 
 static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
@@ -2110,7 +2177,7 @@
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
   if (can_call && kUseBakerReadBarrier) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+    locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
   }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
index 155ff65..97404aa 100644
--- a/compiler/optimizing/intrinsics_x86_64.h
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -36,7 +36,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
@@ -61,7 +61,7 @@
 
   // Define visitor methods.
 
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
   void Visit ## Name(HInvoke* invoke) OVERRIDE;
 #include "intrinsics_list.h"
 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 1b1b3a7..d157509 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -33,8 +33,8 @@
       output_overlaps_(Location::kOutputOverlap),
       stack_mask_(nullptr),
       register_mask_(0),
-      live_registers_(),
-      custom_slow_path_caller_saves_() {
+      live_registers_(RegisterSet::Empty()),
+      custom_slow_path_caller_saves_(RegisterSet::Empty()) {
   instruction->SetLocations(this);
 
   if (NeedsSafepoint()) {
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index c97c4a6..da27928 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -420,7 +420,7 @@
 
 class RegisterSet : public ValueObject {
  public:
-  RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
+  static RegisterSet Empty() { return RegisterSet(); }
 
   void Add(Location loc) {
     if (loc.IsRegister()) {
@@ -465,6 +465,8 @@
   }
 
  private:
+  RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
+
   uint32_t core_registers_;
   uint32_t floating_point_registers_;
 };
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 9cfa89b..ef9bf23 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -25,7 +25,7 @@
 #include "base/stl_util.h"
 #include "intrinsics.h"
 #include "mirror/class-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6d207765..397abde 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,6 +32,7 @@
 #include "handle.h"
 #include "handle_scope.h"
 #include "invoke_type.h"
+#include "intrinsics_enum.h"
 #include "locations.h"
 #include "method_reference.h"
 #include "mirror/class.h"
@@ -3690,17 +3691,6 @@
   DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
 
-enum class Intrinsics {
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
-  k ## Name,
-#include "intrinsics_list.h"
-  kNone,
-  INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
-#undef INTRINSICS_LIST
-#undef OPTIMIZING_INTRINSICS
-};
-std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic);
-
 enum IntrinsicNeedsEnvironmentOrCache {
   kNoEnvironmentOrCache,        // Intrinsic does not require an environment or dex cache.
   kNeedsEnvironmentOrCache      // Intrinsic requires an environment or requires a dex cache.
@@ -3742,8 +3732,8 @@
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
   const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
 
-  InvokeType GetOriginalInvokeType() const {
-    return GetPackedField<OriginalInvokeTypeField>();
+  InvokeType GetInvokeType() const {
+    return GetPackedField<InvokeTypeField>();
   }
 
   Intrinsics GetIntrinsic() const {
@@ -3777,21 +3767,22 @@
 
   bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; }
 
+  ArtMethod* GetResolvedMethod() const { return resolved_method_; }
+
   DECLARE_ABSTRACT_INSTRUCTION(Invoke);
 
  protected:
-  static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits;
-  static constexpr size_t kFieldOriginalInvokeTypeSize =
+  static constexpr size_t kFieldInvokeType = kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldInvokeTypeSize =
       MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
   static constexpr size_t kFieldReturnType =
-      kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize;
+      kFieldInvokeType + kFieldInvokeTypeSize;
   static constexpr size_t kFieldReturnTypeSize =
       MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
   static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize;
   static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1;
   static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-  using OriginalInvokeTypeField =
-      BitField<InvokeType, kFieldOriginalInvokeType, kFieldOriginalInvokeTypeSize>;
+  using InvokeTypeField = BitField<InvokeType, kFieldInvokeType, kFieldInvokeTypeSize>;
   using ReturnTypeField = BitField<Primitive::Type, kFieldReturnType, kFieldReturnTypeSize>;
 
   HInvoke(ArenaAllocator* arena,
@@ -3800,23 +3791,26 @@
           Primitive::Type return_type,
           uint32_t dex_pc,
           uint32_t dex_method_index,
-          InvokeType original_invoke_type)
+          ArtMethod* resolved_method,
+          InvokeType invoke_type)
     : HInstruction(
           SideEffects::AllExceptGCDependency(), dex_pc),  // Assume write/read on all fields/arrays.
       number_of_arguments_(number_of_arguments),
+      resolved_method_(resolved_method),
       inputs_(number_of_arguments + number_of_other_inputs,
               arena->Adapter(kArenaAllocInvokeInputs)),
       dex_method_index_(dex_method_index),
       intrinsic_(Intrinsics::kNone),
       intrinsic_optimizations_(0) {
     SetPackedField<ReturnTypeField>(return_type);
-    SetPackedField<OriginalInvokeTypeField>(original_invoke_type);
+    SetPackedField<InvokeTypeField>(invoke_type);
     SetPackedFlag<kFlagCanThrow>(true);
   }
 
   void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); }
 
   uint32_t number_of_arguments_;
+  ArtMethod* const resolved_method_;
   ArenaVector<HUserRecord<HInstruction*>> inputs_;
   const uint32_t dex_method_index_;
   Intrinsics intrinsic_;
@@ -3842,6 +3836,7 @@
                 return_type,
                 dex_pc,
                 dex_method_index,
+                nullptr,
                 invoke_type) {
   }
 
@@ -3935,10 +3930,10 @@
                         Primitive::Type return_type,
                         uint32_t dex_pc,
                         uint32_t method_index,
-                        MethodReference target_method,
+                        ArtMethod* resolved_method,
                         DispatchInfo dispatch_info,
-                        InvokeType original_invoke_type,
-                        InvokeType optimized_invoke_type,
+                        InvokeType invoke_type,
+                        MethodReference target_method,
                         ClinitCheckRequirement clinit_check_requirement)
       : HInvoke(arena,
                 number_of_arguments,
@@ -3950,10 +3945,10 @@
                 return_type,
                 dex_pc,
                 method_index,
-                original_invoke_type),
+                resolved_method,
+                invoke_type),
         target_method_(target_method),
         dispatch_info_(dispatch_info) {
-    SetPackedField<OptimizedInvokeTypeField>(optimized_invoke_type);
     SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement);
   }
 
@@ -4017,14 +4012,6 @@
   uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); }
   bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); }
 
-  InvokeType GetOptimizedInvokeType() const {
-    return GetPackedField<OptimizedInvokeTypeField>();
-  }
-
-  void SetOptimizedInvokeType(InvokeType invoke_type) {
-    SetPackedField<OptimizedInvokeTypeField>(invoke_type);
-  }
-
   MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
   CodePtrLocation GetCodePtrLocation() const { return dispatch_info_.code_ptr_location; }
   bool IsRecursive() const { return GetMethodLoadKind() == MethodLoadKind::kRecursive; }
@@ -4046,12 +4033,10 @@
     }
   }
   bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; }
-  MethodReference GetTargetMethod() const { return target_method_; }
-  void SetTargetMethod(MethodReference method) { target_method_ = method; }
 
-  int32_t GetStringInitOffset() const {
+  QuickEntrypointEnum GetStringInitEntryPoint() const {
     DCHECK(IsStringInit());
-    return dispatch_info_.method_load_data;
+    return static_cast<QuickEntrypointEnum>(dispatch_info_.method_load_data);
   }
 
   uint64_t GetMethodAddress() const {
@@ -4075,7 +4060,11 @@
 
   // Is this instruction a call to a static method?
   bool IsStatic() const {
-    return GetOriginalInvokeType() == kStatic;
+    return GetInvokeType() == kStatic;
+  }
+
+  MethodReference GetTargetMethod() const {
+    return target_method_;
   }
 
   // Remove the HClinitCheck or the replacement HLoadClass (set as last input by
@@ -4117,26 +4106,18 @@
   void RemoveInputAt(size_t index);
 
  private:
-  static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits;
-  static constexpr size_t kFieldOptimizedInvokeTypeSize =
-      MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
-  static constexpr size_t kFieldClinitCheckRequirement =
-      kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize;
+  static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
   static constexpr size_t kFieldClinitCheckRequirementSize =
       MinimumBitsToStore(static_cast<size_t>(ClinitCheckRequirement::kLast));
   static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits =
       kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize;
   static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
-  using OptimizedInvokeTypeField =
-      BitField<InvokeType, kFieldOptimizedInvokeType, kFieldOptimizedInvokeTypeSize>;
   using ClinitCheckRequirementField = BitField<ClinitCheckRequirement,
                                                kFieldClinitCheckRequirement,
                                                kFieldClinitCheckRequirementSize>;
 
-  // The target method may refer to different dex file or method index than the original
-  // invoke. This happens for sharpened calls and for calls where a method was redeclared
-  // in derived class to increase visibility.
+  // Cached values of the resolved method, to avoid needing the mutator lock.
   MethodReference target_method_;
   DispatchInfo dispatch_info_;
 
@@ -4152,8 +4133,16 @@
                  Primitive::Type return_type,
                  uint32_t dex_pc,
                  uint32_t dex_method_index,
+                 ArtMethod* resolved_method,
                  uint32_t vtable_index)
-      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kVirtual),
+      : HInvoke(arena,
+                number_of_arguments,
+                0u,
+                return_type,
+                dex_pc,
+                dex_method_index,
+                resolved_method,
+                kVirtual),
         vtable_index_(vtable_index) {}
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -4166,6 +4155,7 @@
   DECLARE_INSTRUCTION(InvokeVirtual);
 
  private:
+  // Cached value of the resolved method, to avoid needing the mutator lock.
   const uint32_t vtable_index_;
 
   DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual);
@@ -4178,8 +4168,16 @@
                    Primitive::Type return_type,
                    uint32_t dex_pc,
                    uint32_t dex_method_index,
+                   ArtMethod* resolved_method,
                    uint32_t imt_index)
-      : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kInterface),
+      : HInvoke(arena,
+                number_of_arguments,
+                0u,
+                return_type,
+                dex_pc,
+                dex_method_index,
+                resolved_method,
+                kInterface),
         imt_index_(imt_index) {}
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -4193,6 +4191,7 @@
   DECLARE_INSTRUCTION(InvokeInterface);
 
  private:
+  // Cached value of the resolved method, to avoid needing the mutator lock.
   const uint32_t imt_index_;
 
   DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c5d7611..d3a55dd 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -56,7 +56,6 @@
 #include "dead_code_elimination.h"
 #include "debug/elf_debug_writer.h"
 #include "debug/method_debug_info.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
 #include "driver/compiler_driver-inl.h"
@@ -479,7 +478,7 @@
   } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) {
     return new (arena) InstructionSimplifier(graph, stats, pass_name.c_str());
   } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) {
-    return new (arena) IntrinsicsRecognizer(graph, driver, stats);
+    return new (arena) IntrinsicsRecognizer(graph, stats);
   } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) {
     CHECK(most_recent_side_effects != nullptr);
     return new (arena) LICM(graph, *most_recent_side_effects, stats);
@@ -743,7 +742,7 @@
       graph, stats, "instruction_simplifier$after_bce");
   InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
       graph, stats, "instruction_simplifier$before_codegen");
-  IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver, stats);
+  IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
 
   HOptimization* optimizations1[] = {
     intrinsics,
@@ -899,7 +898,7 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::ClassLoader> loader(hs.NewHandle(
-        soa.Decode<mirror::ClassLoader*>(class_loader)));
+        soa.Decode<mirror::ClassLoader>(class_loader)));
     method = compiler_driver->ResolveMethod(
         soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
   }
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index dd5cb1c..2a23c92 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -22,7 +22,7 @@
 #include "common_compiler_test.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
-#include "handle_scope-inl.h"
+#include "handle_scope.h"
 #include "scoped_thread_state_change.h"
 #include "ssa_builder.h"
 #include "ssa_liveness_analysis.h"
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 4289cf7..5a47df1 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -20,7 +20,7 @@
 #include "class_linker-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index e64c005..a4a3e06 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -31,7 +31,7 @@
 #include "mirror/string.h"
 #include "nodes.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -61,44 +61,28 @@
     return;
   }
 
-  // TODO: Avoid CompilerDriver.
-  InvokeType original_invoke_type = invoke->GetOriginalInvokeType();
-  InvokeType optimized_invoke_type = original_invoke_type;
-  MethodReference target_method(&graph_->GetDexFile(), invoke->GetDexMethodIndex());
-  int vtable_idx;
-  uintptr_t direct_code, direct_method;
-  bool success = compiler_driver_->ComputeInvokeInfo(
-      &compilation_unit_,
-      invoke->GetDexPc(),
-      false /* update_stats: already updated in builder */,
-      true /* enable_devirtualization */,
-      &optimized_invoke_type,
-      &target_method,
-      &vtable_idx,
-      &direct_code,
-      &direct_method);
-  if (!success) {
-    // TODO: try using kDexCachePcRelative. It's always a valid method load
-    // kind as long as it's supported by the codegen
-    return;
-  }
-  invoke->SetOptimizedInvokeType(optimized_invoke_type);
-  invoke->SetTargetMethod(target_method);
+  HGraph* outer_graph = codegen_->GetGraph();
+  ArtMethod* compiling_method = graph_->GetArtMethod();
 
   HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
   HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
   uint64_t method_load_data = 0u;
   uint64_t direct_code_ptr = 0u;
 
-  HGraph* outer_graph = codegen_->GetGraph();
-  if (target_method.dex_file == &outer_graph->GetDexFile() &&
-      target_method.dex_method_index == outer_graph->GetMethodIdx()) {
+  if (invoke->GetResolvedMethod() == outer_graph->GetArtMethod()) {
+    DCHECK(outer_graph->GetArtMethod() != nullptr);
     method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
     code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
   } else {
-    bool use_pc_relative_instructions =
-        ((direct_method == 0u || direct_code == static_cast<uintptr_t>(-1))) &&
-        ContainsElement(compiler_driver_->GetDexFilesForOatFile(), target_method.dex_file);
+    uintptr_t direct_code, direct_method;
+    {
+      ScopedObjectAccess soa(Thread::Current());
+      compiler_driver_->GetCodeAndMethodForDirectCall(
+          (compiling_method == nullptr) ? nullptr : compiling_method->GetDeclaringClass(),
+          invoke->GetResolvedMethod(),
+          &direct_code,
+          &direct_method);
+    }
     if (direct_method != 0u) {  // Should we use a direct pointer to the method?
       // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
       // kDirectAddress would be fine for image methods, we don't support it at the moment.
@@ -110,13 +94,12 @@
         method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup;
       }
     } else {  // Use dex cache.
-      DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
-      if (use_pc_relative_instructions) {  // Can we use PC-relative access to the dex cache arrays?
-        DCHECK(!Runtime::Current()->UseJitCompilation());
+      if (!Runtime::Current()->UseJitCompilation()) {
+        // Use PC-relative access to the dex cache arrays.
         method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
         DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
                                     &graph_->GetDexFile());
-        method_load_data = layout.MethodOffset(target_method.dex_method_index);
+        method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
       } else {  // We must go through the ArtMethod's pointer to resolved methods.
         method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
       }
@@ -125,10 +108,11 @@
       // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
       // while kCallDirect would be fine for image methods, we don't support it at the moment.
       DCHECK(!Runtime::Current()->UseJitCompilation());
+      const DexFile* dex_file_of_callee = invoke->GetTargetMethod().dex_file;
       if (direct_code != static_cast<uintptr_t>(-1)) {  // Is the code pointer known now?
         code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
         direct_code_ptr = direct_code;
-      } else if (use_pc_relative_instructions) {
+      } else if (ContainsElement(compiler_driver_->GetDexFilesForOatFile(), dex_file_of_callee)) {
         // Use PC-relative calls for invokes within a multi-dex oat file.
         code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
       } else {  // The direct pointer will be known at link time.
@@ -151,8 +135,7 @@
       method_load_kind, code_ptr_location, method_load_data, direct_code_ptr
   };
   HInvokeStaticOrDirect::DispatchInfo dispatch_info =
-      codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info,
-                                                         invoke->GetTargetMethod());
+      codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
   invoke->SetDispatchInfo(dispatch_info);
 }
 
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index f7dc112..03807ba 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -163,18 +163,12 @@
 // Replace inputs of `phi` to match its type. Return false if conflict is identified.
 bool SsaBuilder::TypeInputsOfPhi(HPhi* phi, ArenaVector<HPhi*>* worklist) {
   Primitive::Type common_type = phi->GetType();
-  if (common_type == Primitive::kPrimVoid || Primitive::IsIntegralType(common_type)) {
-    // Phi either contains only other untyped phis (common_type == kPrimVoid),
-    // or `common_type` is integral and we do not need to retype ambiguous inputs
-    // because they are always constructed with the integral type candidate.
+  if (Primitive::IsIntegralType(common_type)) {
+    // We do not need to retype ambiguous inputs because they are always constructed
+    // with the integral type candidate.
     if (kIsDebugBuild) {
       for (HInstruction* input : phi->GetInputs()) {
-        if (common_type == Primitive::kPrimVoid) {
-          DCHECK(input->IsPhi() && input->GetType() == Primitive::kPrimVoid);
-        } else {
-          DCHECK((input->IsPhi() && input->GetType() == Primitive::kPrimVoid) ||
-                 HPhi::ToPhiType(input->GetType()) == common_type);
-        }
+        DCHECK(HPhi::ToPhiType(input->GetType()) == common_type);
       }
     }
     // Inputs did not need to be replaced, hence no conflict. Report success.
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 11c18b0..05a5d0f 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -31,6 +31,7 @@
                 // SANITIZE_TARGET mode.
                 // Bug: 22233158
                 address: false,
+                coverage: false,
             },
         },
     },
@@ -49,6 +50,7 @@
     shared_libs: [
         "libart",
         "libart-compiler",
+        "libbase",
         "libsigchain",
     ],
 }
@@ -62,6 +64,7 @@
     shared_libs: [
         "libartd",
         "libartd-compiler",
+        "libbase",
         "libsigchain",
     ],
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d99d2d6..1ddf961 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -49,7 +49,6 @@
 #include "compiler_callbacks.h"
 #include "debug/elf_debug_writer.h"
 #include "debug/method_debug_info.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
 #include "dex_file-inl.h"
@@ -64,6 +63,8 @@
 #include "interpreter/unstarted_runtime.h"
 #include "jit/offline_profiling_info.h"
 #include "leb128.h"
+#include "linker/buffered_output_stream.h"
+#include "linker/file_output_stream.h"
 #include "linker/multi_oat_relative_patcher.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -75,7 +76,7 @@
 #include "runtime.h"
 #include "runtime_options.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utils.h"
 #include "verifier/verifier_deps.h"
 #include "well_known_classes.h"
@@ -448,7 +449,11 @@
     //       it's rather easy to hang in unwinding.
     //       LogLine also avoids ART logging lock issues, as it's really only a wrapper around
     //       logcat logging or stderr output.
-    LogMessage::LogLine(__FILE__, __LINE__, LogSeverity::FATAL, message.c_str());
+    android::base::LogMessage::LogLine(__FILE__,
+                                       __LINE__,
+                                       android::base::LogId::DEFAULT,
+                                       LogSeverity::FATAL,
+                                       message.c_str());
     exit(1);
   }
 
@@ -503,7 +508,6 @@
       image_patch_delta_(0),
       key_value_store_(nullptr),
       verification_results_(nullptr),
-      method_inliner_map_(),
       runtime_(nullptr),
       thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
       start_ns_(NanoTime()),
@@ -986,6 +990,8 @@
       }
     }
 
+    char_backing_storage_.reserve((dex_locations_.size() - 1) * 2);
+
     // Now create the other names. Use a counted loop to skip the first one.
     for (size_t i = 1; i < dex_locations_.size(); ++i) {
       // TODO: Make everything properly std::string.
@@ -1366,7 +1372,6 @@
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
     callbacks_.reset(new QuickCompilerCallbacks(
         verification_results_.get(),
-        &method_inliner_map_,
         IsBootImage() ?
             CompilerCallbacks::CallbackMode::kCompileBootImage :
             CompilerCallbacks::CallbackMode::kCompileApp));
@@ -1384,7 +1389,10 @@
     if (IsBootImage() && image_filenames_.size() > 1) {
       // If we're compiling the boot image, store the boot classpath into the Key-Value store.
       // We need this for the multi-image case.
-      key_value_store_->Put(OatHeader::kBootClassPathKey, GetMultiImageBootClassPath());
+      key_value_store_->Put(OatHeader::kBootClassPathKey,
+                            gc::space::ImageSpace::GetMultiImageBootClassPath(dex_locations_,
+                                                                              oat_filenames_,
+                                                                              image_filenames_));
     }
 
     if (!IsBootImage()) {
@@ -1558,7 +1566,7 @@
       ScopedObjectAccess soa(self);
       dex_caches_.push_back(soa.AddLocalReference<jobject>(
           class_linker->RegisterDexFile(*dex_file,
-                                        soa.Decode<mirror::ClassLoader*>(class_loader_))));
+                                        soa.Decode<mirror::ClassLoader>(class_loader_).Decode())));
     }
 
     return true;
@@ -1623,7 +1631,6 @@
 
     driver_.reset(new CompilerDriver(compiler_options_.get(),
                                      verification_results_.get(),
-                                     &method_inliner_map_,
                                      compiler_kind_,
                                      instruction_set_,
                                      instruction_set_features_.get(),
@@ -1753,6 +1760,28 @@
       }
     }
 
+    {
+      TimingLogger::ScopedTiming t2("dex2oat Write VDEX", timings_);
+      DCHECK(IsBootImage() || oat_files_.size() == 1u);
+      DCHECK_EQ(IsBootImage(), verifier_deps_ == nullptr);
+      for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
+        File* vdex_file = vdex_files_[i].get();
+        std::unique_ptr<BufferedOutputStream> vdex_out(
+            MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(vdex_file)));
+
+        if (!oat_writers_[i]->WriteVerifierDeps(vdex_out.get(), verifier_deps_.get())) {
+          LOG(ERROR) << "Failed to write verifier dependencies into VDEX " << vdex_file->GetPath();
+          return false;
+        }
+
+        // VDEX finalized, seek back to the beginning and write the header.
+        if (!oat_writers_[i]->WriteVdexHeader(vdex_out.get())) {
+          LOG(ERROR) << "Failed to write vdex header into VDEX " << vdex_file->GetPath();
+          return false;
+        }
+      }
+    }
+
     linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get());
     {
       TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
@@ -2028,49 +2057,6 @@
     return result;
   }
 
-  std::string GetMultiImageBootClassPath() {
-    DCHECK(IsBootImage());
-    DCHECK_GT(oat_filenames_.size(), 1u);
-    // If the image filename was adapted (e.g., for our tests), we need to change this here,
-    // too, but need to strip all path components (they will be re-established when loading).
-    std::ostringstream bootcp_oss;
-    bool first_bootcp = true;
-    for (size_t i = 0; i < dex_locations_.size(); ++i) {
-      if (!first_bootcp) {
-        bootcp_oss << ":";
-      }
-
-      std::string dex_loc = dex_locations_[i];
-      std::string image_filename = image_filenames_[i];
-
-      // Use the dex_loc path, but the image_filename name (without path elements).
-      size_t dex_last_slash = dex_loc.rfind('/');
-
-      // npos is max(size_t). That makes this a bit ugly.
-      size_t image_last_slash = image_filename.rfind('/');
-      size_t image_last_at = image_filename.rfind('@');
-      size_t image_last_sep = (image_last_slash == std::string::npos)
-                                  ? image_last_at
-                                  : (image_last_at == std::string::npos)
-                                        ? std::string::npos
-                                        : std::max(image_last_slash, image_last_at);
-      // Note: whenever image_last_sep == npos, +1 overflow means using the full string.
-
-      if (dex_last_slash == std::string::npos) {
-        dex_loc = image_filename.substr(image_last_sep + 1);
-      } else {
-        dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
-            image_filename.substr(image_last_sep + 1);
-      }
-
-      // Image filenames already end with .art, no need to replace.
-
-      bootcp_oss << dex_loc;
-      first_bootcp = false;
-    }
-    return bootcp_oss.str();
-  }
-
   std::vector<std::string> GetClassPathLocations(const std::string& class_path) {
     // This function is used only for apps and for an app we have exactly one oat file.
     DCHECK(!IsBootImage());
@@ -2552,7 +2538,6 @@
 
   std::unique_ptr<VerificationResults> verification_results_;
 
-  DexFileToMethodInlinerMap method_inliner_map_;
   std::unique_ptr<QuickCompilerCallbacks> callbacks_;
 
   std::unique_ptr<Runtime> runtime_;
@@ -2604,6 +2589,7 @@
   std::vector<std::unique_ptr<ElfWriter>> elf_writers_;
   std::vector<std::unique_ptr<OatWriter>> oat_writers_;
   std::vector<OutputStream*> rodata_;
+  std::vector<std::unique_ptr<OutputStream>> vdex_out_;
   std::unique_ptr<ImageWriter> image_writer_;
   std::unique_ptr<CompilerDriver> driver_;
 
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index 64f2299..3e589f7 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -22,7 +22,10 @@
         "dexdump.cc",
     ],
     cflags: ["-Wall"],
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libbase",
+    ],
 }
 
 art_cc_test {
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 163cb01..296cdb6 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -18,10 +18,14 @@
     srcs: [
         "dexlayout_main.cc",
         "dexlayout.cc",
+        "dex_ir.cc",
         "dex_ir_builder.cc",
     ],
     cflags: ["-Wall"],
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libbase",
+    ],
 }
 
 art_cc_test {
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
new file mode 100644
index 0000000..aff03cd
--- /dev/null
+++ b/dexlayout/dex_ir.cc
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 file of the dexlayout utility.
+ *
+ * This is a tool to read dex files into an internal representation,
+ * reorganize the representation, and emit dex files with a better
+ * file layout.
+ */
+
+#include "dex_ir.h"
+#include "dex_ir_builder.h"
+
+namespace art {
+namespace dex_ir {
+
+static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) {
+  uint64_t value = 0;
+  for (uint32_t i = 0; i <= length; i++) {
+    value |= static_cast<uint64_t>(*(*data)++) << (i * 8);
+  }
+  if (sign_extend) {
+    int shift = (7 - length) * 8;
+    return (static_cast<int64_t>(value) << shift) >> shift;
+  }
+  return value;
+}
+
+static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
+  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
+  PositionInfoVector& positions = debug_info->GetPositionInfo();
+  positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
+  return false;
+}
+
+static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
+  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
+  LocalInfoVector& locals = debug_info->GetLocalInfo();
+  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
+  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
+  locals.push_back(std::unique_ptr<LocalInfo>(
+      new LocalInfo(name, entry.descriptor_, signature, entry.start_address_,
+                    entry.end_address_, entry.reg_)));
+}
+
+EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) {
+  const uint8_t encoded_value = *(*data)++;
+  const uint8_t type = encoded_value & 0x1f;
+  EncodedValue* item = new EncodedValue(type);
+  ReadEncodedValue(data, type, encoded_value >> 5, item);
+  return item;
+}
+
+EncodedValue* Collections::ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length) {
+  EncodedValue* item = new EncodedValue(type);
+  ReadEncodedValue(data, type, length, item);
+  return item;
+}
+
+void Collections::ReadEncodedValue(
+    const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item) {
+  switch (type) {
+    case DexFile::kDexAnnotationByte:
+      item->SetByte(static_cast<int8_t>(ReadVarWidth(data, length, false)));
+      break;
+    case DexFile::kDexAnnotationShort:
+      item->SetShort(static_cast<int16_t>(ReadVarWidth(data, length, true)));
+      break;
+    case DexFile::kDexAnnotationChar:
+      item->SetChar(static_cast<uint16_t>(ReadVarWidth(data, length, false)));
+      break;
+    case DexFile::kDexAnnotationInt:
+      item->SetInt(static_cast<int32_t>(ReadVarWidth(data, length, true)));
+      break;
+    case DexFile::kDexAnnotationLong:
+      item->SetLong(static_cast<int64_t>(ReadVarWidth(data, length, true)));
+      break;
+    case DexFile::kDexAnnotationFloat: {
+      // Fill on right.
+      union {
+        float f;
+        uint32_t data;
+      } conv;
+      conv.data = static_cast<uint32_t>(ReadVarWidth(data, length, false)) << (3 - length) * 8;
+      item->SetFloat(conv.f);
+      break;
+    }
+    case DexFile::kDexAnnotationDouble: {
+      // Fill on right.
+      union {
+        double d;
+        uint64_t data;
+      } conv;
+      conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8;
+      item->SetDouble(conv.d);
+      break;
+    }
+    case DexFile::kDexAnnotationString: {
+      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->SetStringId(GetStringId(string_index));
+      break;
+    }
+    case DexFile::kDexAnnotationType: {
+      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->SetTypeId(GetTypeId(string_index));
+      break;
+    }
+    case DexFile::kDexAnnotationField:
+    case DexFile::kDexAnnotationEnum: {
+      const uint32_t field_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->SetFieldId(GetFieldId(field_index));
+      break;
+    }
+    case DexFile::kDexAnnotationMethod: {
+      const uint32_t method_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->SetMethodId(GetMethodId(method_index));
+      break;
+    }
+    case DexFile::kDexAnnotationArray: {
+      EncodedValueVector* values = new EncodedValueVector();
+      const uint32_t size = DecodeUnsignedLeb128(data);
+      // Decode all elements.
+      for (uint32_t i = 0; i < size; i++) {
+        values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(data)));
+      }
+      item->SetEncodedArray(new EncodedArrayItem(values));
+      break;
+    }
+    case DexFile::kDexAnnotationAnnotation: {
+      AnnotationElementVector* elements = new AnnotationElementVector();
+      const uint32_t type_idx = DecodeUnsignedLeb128(data);
+      const uint32_t size = DecodeUnsignedLeb128(data);
+      // Decode all name=value pairs.
+      for (uint32_t i = 0; i < size; i++) {
+        const uint32_t name_index = DecodeUnsignedLeb128(data);
+        elements->push_back(std::unique_ptr<AnnotationElement>(
+            new AnnotationElement(GetStringId(name_index), ReadEncodedValue(data))));
+      }
+      item->SetEncodedAnnotation(new EncodedAnnotation(GetTypeId(type_idx), elements));
+      break;
+    }
+    case DexFile::kDexAnnotationNull:
+      break;
+    case DexFile::kDexAnnotationBoolean:
+      item->SetBoolean(length != 0);
+      break;
+    default:
+      break;
+  }
+}
+
+void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) {
+  const DexFile::StringId& disk_string_id = dex_file.GetStringId(i);
+  StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id));
+  string_datas_.AddItem(string_data, disk_string_id.string_data_off_);
+
+  StringId* string_id = new StringId(string_data);
+  string_ids_.AddIndexedItem(string_id, StringIdsOffset() + i * StringId::ItemSize(), i);
+}
+
+void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) {
+  const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(i);
+  TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_));
+  type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i);
+}
+
+void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
+  const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
+  const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
+  TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_, true);
+
+  ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_),
+                                  GetTypeId(disk_proto_id.return_type_idx_),
+                                  parameter_type_list);
+  proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i);
+}
+
+void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) {
+  const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
+  FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_),
+                                  GetTypeId(disk_field_id.type_idx_),
+                                  GetStringId(disk_field_id.name_idx_));
+  field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i);
+}
+
+void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
+  const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
+  MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_),
+                                     GetProtoId(disk_method_id.proto_idx_),
+                                     GetStringId(disk_method_id.name_idx_));
+  method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
+}
+
+void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) {
+  const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i);
+  const TypeId* class_type = GetTypeId(disk_class_def.class_idx_);
+  uint32_t access_flags = disk_class_def.access_flags_;
+  const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
+
+  const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
+  TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_, false);
+
+  const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
+  // Annotations.
+  AnnotationsDirectoryItem* annotations = nullptr;
+  const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
+      dex_file.GetAnnotationsDirectory(disk_class_def);
+  if (disk_annotations_directory_item != nullptr) {
+    annotations = CreateAnnotationsDirectoryItem(
+        dex_file, disk_annotations_directory_item, disk_class_def.annotations_off_);
+  }
+  // Static field initializers.
+  const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def);
+  EncodedArrayItem* static_values =
+      CreateEncodedArrayItem(static_data, disk_class_def.static_values_off_);
+  ClassData* class_data = CreateClassData(
+      dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_);
+  ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list,
+                                     source_file, annotations, static_values, class_data);
+  class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i);
+}
+
+TypeList* Collections::CreateTypeList(
+    const DexFile::TypeList* dex_type_list, uint32_t offset, bool allow_empty) {
+  if (dex_type_list == nullptr && !allow_empty) {
+    return nullptr;
+  }
+  // TODO: Create more efficient lookup for existing type lists.
+  for (std::unique_ptr<TypeList>& type_list : TypeLists()) {
+    if (type_list->GetOffset() == offset) {
+      return type_list.get();
+    }
+  }
+  TypeIdVector* type_vector = new TypeIdVector();
+  uint32_t size = dex_type_list == nullptr ? 0 : dex_type_list->Size();
+  for (uint32_t index = 0; index < size; ++index) {
+    type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_));
+  }
+  TypeList* new_type_list = new TypeList(type_vector);
+  type_lists_.AddItem(new_type_list, offset);
+  return new_type_list;
+}
+
+EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset) {
+  if (static_data == nullptr) {
+    return nullptr;
+  }
+  uint32_t size = DecodeUnsignedLeb128(&static_data);
+  EncodedValueVector* values = new EncodedValueVector();
+  for (uint32_t i = 0; i < size; ++i) {
+    values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(&static_data)));
+  }
+  // TODO: Calculate the size of the encoded array.
+  EncodedArrayItem* encoded_array_item = new EncodedArrayItem(values);
+  encoded_array_items_.AddItem(encoded_array_item, offset);
+  return encoded_array_item;
+}
+
+AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation,
+                                                  uint32_t offset) {
+  uint8_t visibility = annotation->visibility_;
+  const uint8_t* annotation_data = annotation->annotation_;
+  EncodedValue* encoded_value =
+      ReadEncodedValue(&annotation_data, DexFile::kDexAnnotationAnnotation, 0);
+  // TODO: Calculate the size of the annotation.
+  AnnotationItem* annotation_item =
+      new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation());
+  annotation_items_.AddItem(annotation_item, offset);
+  return annotation_item;
+}
+
+
+AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file,
+    const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset) {
+  if (disk_annotations_item.size_ == 0) {
+    return nullptr;
+  }
+  std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>();
+  for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
+    const DexFile::AnnotationItem* annotation =
+        dex_file.GetAnnotationItem(&disk_annotations_item, i);
+    if (annotation == nullptr) {
+      continue;
+    }
+    AnnotationItem* annotation_item =
+        CreateAnnotationItem(annotation, disk_annotations_item.entries_[i]);
+    items->push_back(annotation_item);
+  }
+  AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items);
+  annotation_set_items_.AddItem(annotation_set_item, offset);
+  return annotation_set_item;
+}
+
+AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file,
+    const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) {
+  const DexFile::AnnotationSetItem* class_set_item =
+      dex_file.GetClassAnnotationSet(disk_annotations_item);
+  AnnotationSetItem* class_annotation = nullptr;
+  if (class_set_item != nullptr) {
+    uint32_t offset = disk_annotations_item->class_annotations_off_;
+    class_annotation = CreateAnnotationSetItem(dex_file, *class_set_item, offset);
+  }
+  const DexFile::FieldAnnotationsItem* fields =
+      dex_file.GetFieldAnnotations(disk_annotations_item);
+  FieldAnnotationVector* field_annotations = nullptr;
+  if (fields != nullptr) {
+    field_annotations = new FieldAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) {
+      FieldId* field_id = GetFieldId(fields[i].field_idx_);
+      const DexFile::AnnotationSetItem* field_set_item =
+          dex_file.GetFieldAnnotationSetItem(fields[i]);
+      uint32_t annotation_set_offset = fields[i].annotations_off_;
+      AnnotationSetItem* annotation_set_item =
+          CreateAnnotationSetItem(dex_file, *field_set_item, annotation_set_offset);
+      field_annotations->push_back(std::unique_ptr<FieldAnnotation>(
+          new FieldAnnotation(field_id, annotation_set_item)));
+    }
+  }
+  const DexFile::MethodAnnotationsItem* methods =
+      dex_file.GetMethodAnnotations(disk_annotations_item);
+  MethodAnnotationVector* method_annotations = nullptr;
+  if (methods != nullptr) {
+    method_annotations = new MethodAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) {
+      MethodId* method_id = GetMethodId(methods[i].method_idx_);
+      const DexFile::AnnotationSetItem* method_set_item =
+          dex_file.GetMethodAnnotationSetItem(methods[i]);
+      uint32_t annotation_set_offset = methods[i].annotations_off_;
+      AnnotationSetItem* annotation_set_item =
+          CreateAnnotationSetItem(dex_file, *method_set_item, annotation_set_offset);
+      method_annotations->push_back(std::unique_ptr<MethodAnnotation>(
+          new MethodAnnotation(method_id, annotation_set_item)));
+    }
+  }
+  const DexFile::ParameterAnnotationsItem* parameters =
+      dex_file.GetParameterAnnotations(disk_annotations_item);
+  ParameterAnnotationVector* parameter_annotations = nullptr;
+  if (parameters != nullptr) {
+    parameter_annotations = new ParameterAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) {
+      MethodId* method_id = GetMethodId(parameters[i].method_idx_);
+      const DexFile::AnnotationSetRefList* list =
+          dex_file.GetParameterAnnotationSetRefList(&parameters[i]);
+      parameter_annotations->push_back(std::unique_ptr<ParameterAnnotation>(
+          GenerateParameterAnnotation(dex_file, method_id, list, parameters[i].annotations_off_)));
+    }
+  }
+  // TODO: Calculate the size of the annotations directory.
+  AnnotationsDirectoryItem* annotations_directory_item = new AnnotationsDirectoryItem(
+      class_annotation, field_annotations, method_annotations, parameter_annotations);
+  annotations_directory_items_.AddItem(annotations_directory_item, offset);
+  return annotations_directory_item;
+}
+
+ParameterAnnotation* Collections::GenerateParameterAnnotation(
+    const DexFile& dex_file, MethodId* method_id,
+    const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) {
+  std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>();
+  for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
+    const DexFile::AnnotationSetItem* annotation_set_item =
+        dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
+    uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
+    annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset));
+  }
+  AnnotationSetRefList* new_ref_list = new AnnotationSetRefList(annotations);
+  annotation_set_ref_lists_.AddItem(new_ref_list, offset);
+  return new ParameterAnnotation(method_id, new_ref_list);
+}
+
+CodeItem* Collections::CreateCodeItem(const DexFile& dex_file,
+                                      const DexFile::CodeItem& disk_code_item, uint32_t offset) {
+  uint16_t registers_size = disk_code_item.registers_size_;
+  uint16_t ins_size = disk_code_item.ins_size_;
+  uint16_t outs_size = disk_code_item.outs_size_;
+  uint32_t tries_size = disk_code_item.tries_size_;
+
+  // TODO: Calculate the size of the debug info.
+  const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
+  DebugInfoItem* debug_info = nullptr;
+  if (debug_info_stream != nullptr) {
+    debug_info = new DebugInfoItem();
+    debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_);
+  }
+
+  uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
+  uint16_t* insns = new uint16_t[insns_size];
+  memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
+
+  TryItemVector* tries = nullptr;
+  if (tries_size > 0) {
+    tries = new TryItemVector();
+    for (uint32_t i = 0; i < tries_size; ++i) {
+      const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
+      uint32_t start_addr = disk_try_item->start_addr_;
+      uint16_t insn_count = disk_try_item->insn_count_;
+      CatchHandlerVector* handlers = new CatchHandlerVector();
+      for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
+        const uint16_t type_index = it.GetHandlerTypeIndex();
+        const TypeId* type_id = GetTypeIdOrNullPtr(type_index);
+        handlers->push_back(std::unique_ptr<const CatchHandler>(
+            new CatchHandler(type_id, it.GetHandlerAddress())));
+      }
+      TryItem* try_item = new TryItem(start_addr, insn_count, handlers);
+      tries->push_back(std::unique_ptr<const TryItem>(try_item));
+    }
+  }
+  // TODO: Calculate the size of the code item.
+  CodeItem* code_item =
+      new CodeItem(registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries);
+  code_items_.AddItem(code_item, offset);
+  return code_item;
+}
+
+MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
+  MethodId* method_item = GetMethodId(cdii.GetMemberIndex());
+  uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+  const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
+  CodeItem* code_item = nullptr;
+  DebugInfoItem* debug_info = nullptr;
+  if (disk_code_item != nullptr) {
+    code_item = CreateCodeItem(dex_file, *disk_code_item, cdii.GetMethodCodeItemOffset());
+    debug_info = code_item->DebugInfo();
+  }
+  if (debug_info != nullptr) {
+    bool is_static = (access_flags & kAccStatic) != 0;
+    dex_file.DecodeDebugLocalInfo(
+        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
+    dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
+  }
+  return new MethodItem(access_flags, method_item, code_item);
+}
+
+ClassData* Collections::CreateClassData(
+    const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) {
+  // Read the fields and methods defined by the class, resolving the circular reference from those
+  // to classes by setting class at the same time.
+  ClassData* class_data = nullptr;
+  if (encoded_data != nullptr) {
+    ClassDataItemIterator cdii(dex_file, encoded_data);
+    // Static fields.
+    FieldItemVector* static_fields = new FieldItemVector();
+    for (uint32_t i = 0; cdii.HasNextStaticField(); i++, cdii.Next()) {
+      FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
+      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+      static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+    }
+    // Instance fields.
+    FieldItemVector* instance_fields = new FieldItemVector();
+    for (uint32_t i = 0; cdii.HasNextInstanceField(); i++, cdii.Next()) {
+      FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
+      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+      instance_fields->push_back(
+          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+    }
+    // Direct methods.
+    MethodItemVector* direct_methods = new MethodItemVector();
+    for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
+      direct_methods->push_back(
+          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+    }
+    // Virtual methods.
+    MethodItemVector* virtual_methods = new MethodItemVector();
+    for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
+      virtual_methods->push_back(
+          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+    }
+    // TODO: Calculate the size of the class data.
+    class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
+    class_datas_.AddItem(class_data, offset);
+  }
+  return class_data;
+}
+
+}  // namespace dex_ir
+}  // namespace art
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index cbb4404..6ae9f1c 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -23,18 +23,23 @@
 #include <stdint.h>
 
 #include "dex_file-inl.h"
+#include "leb128.h"
 
 namespace art {
 namespace dex_ir {
 
 // Forward declarations for classes used in containers or pointed to.
+class AnnotationItem;
 class AnnotationsDirectoryItem;
 class AnnotationSetItem;
-class ArrayItem;
+class AnnotationSetRefList;
 class ClassData;
 class ClassDef;
 class CodeItem;
 class DebugInfoItem;
+class EncodedAnnotation;
+class EncodedArrayItem;
+class EncodedValue;
 class FieldId;
 class FieldItem;
 class Header;
@@ -42,10 +47,22 @@
 class MapItem;
 class MethodId;
 class MethodItem;
+class ParameterAnnotation;
 class ProtoId;
+class StringData;
 class StringId;
 class TryItem;
 class TypeId;
+class TypeList;
+
+// Item size constants.
+static constexpr size_t kHeaderItemSize = 112;
+static constexpr size_t kStringIdItemSize = 4;
+static constexpr size_t kTypeIdItemSize = 4;
+static constexpr size_t kProtoIdItemSize = 12;
+static constexpr size_t kFieldIdItemSize = 8;
+static constexpr size_t kMethodIdItemSize = 8;
+static constexpr size_t kClassDefItemSize = 32;
 
 // Visitor support
 class AbstractDispatcher {
@@ -54,6 +71,7 @@
   virtual ~AbstractDispatcher() { }
 
   virtual void Dispatch(Header* header) = 0;
+  virtual void Dispatch(const StringData* string_data) = 0;
   virtual void Dispatch(const StringId* string_id) = 0;
   virtual void Dispatch(const TypeId* type_id) = 0;
   virtual void Dispatch(const ProtoId* proto_id) = 0;
@@ -63,11 +81,13 @@
   virtual void Dispatch(ClassDef* class_def) = 0;
   virtual void Dispatch(FieldItem* field_item) = 0;
   virtual void Dispatch(MethodItem* method_item) = 0;
-  virtual void Dispatch(ArrayItem* array_item) = 0;
+  virtual void Dispatch(EncodedArrayItem* array_item) = 0;
   virtual void Dispatch(CodeItem* code_item) = 0;
   virtual void Dispatch(TryItem* try_item) = 0;
   virtual void Dispatch(DebugInfoItem* debug_info_item) = 0;
+  virtual void Dispatch(AnnotationItem* annotation_item) = 0;
   virtual void Dispatch(AnnotationSetItem* annotation_set_item) = 0;
+  virtual void Dispatch(AnnotationSetRefList* annotation_set_ref_list) = 0;
   virtual void Dispatch(AnnotationsDirectoryItem* annotations_directory_item) = 0;
   virtual void Dispatch(MapList* map_list) = 0;
   virtual void Dispatch(MapItem* map_item) = 0;
@@ -82,9 +102,14 @@
   CollectionWithOffset() = default;
   std::vector<std::unique_ptr<T>>& Collection() { return collection_; }
   // Read-time support methods
-  void AddWithPosition(uint32_t position, T* object) {
+  void AddItem(T* object, uint32_t offset) {
+    object->SetOffset(offset);
     collection_.push_back(std::unique_ptr<T>(object));
-    collection_.back()->SetOffset(position);
+  }
+  void AddIndexedItem(T* object, uint32_t offset, uint32_t index) {
+    object->SetOffset(offset);
+    object->SetIndex(index);
+    collection_.push_back(std::unique_ptr<T>(object));
   }
   // Ordinary object insertion into collection.
   void Insert(T object ATTRIBUTE_UNUSED) {
@@ -98,18 +123,160 @@
  private:
   std::vector<std::unique_ptr<T>> collection_;
   uint32_t offset_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(CollectionWithOffset);
 };
 
+class Collections {
+ public:
+  Collections() = default;
+
+  std::vector<std::unique_ptr<StringId>>& StringIds() { return string_ids_.Collection(); }
+  std::vector<std::unique_ptr<TypeId>>& TypeIds() { return type_ids_.Collection(); }
+  std::vector<std::unique_ptr<ProtoId>>& ProtoIds() { return proto_ids_.Collection(); }
+  std::vector<std::unique_ptr<FieldId>>& FieldIds() { return field_ids_.Collection(); }
+  std::vector<std::unique_ptr<MethodId>>& MethodIds() { return method_ids_.Collection(); }
+  std::vector<std::unique_ptr<ClassDef>>& ClassDefs() { return class_defs_.Collection(); }
+
+  std::vector<std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); }
+  std::vector<std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems()
+      { return encoded_array_items_.Collection(); }
+
+  void CreateStringId(const DexFile& dex_file, uint32_t i);
+  void CreateTypeId(const DexFile& dex_file, uint32_t i);
+  void CreateProtoId(const DexFile& dex_file, uint32_t i);
+  void CreateFieldId(const DexFile& dex_file, uint32_t i);
+  void CreateMethodId(const DexFile& dex_file, uint32_t i);
+  void CreateClassDef(const DexFile& dex_file, uint32_t i);
+
+  TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset, bool allow_empty);
+  EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset);
+  AnnotationItem* CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset);
+  AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file,
+      const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset);
+  AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file,
+      const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset);
+  CodeItem* CreateCodeItem(
+      const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset);
+  ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset);
+
+  StringId* GetStringId(uint32_t index) { return StringIds()[index].get(); }
+  TypeId* GetTypeId(uint32_t index) { return TypeIds()[index].get(); }
+  ProtoId* GetProtoId(uint32_t index) { return ProtoIds()[index].get(); }
+  FieldId* GetFieldId(uint32_t index) { return FieldIds()[index].get(); }
+  MethodId* GetMethodId(uint32_t index) { return MethodIds()[index].get(); }
+  ClassDef* GetClassDef(uint32_t index) { return ClassDefs()[index].get(); }
+
+  StringId* GetStringIdOrNullPtr(uint32_t index) {
+    return index == DexFile::kDexNoIndex ? nullptr : GetStringId(index);
+  }
+  TypeId* GetTypeIdOrNullPtr(uint16_t index) {
+    return index == DexFile::kDexNoIndex16 ? nullptr : GetTypeId(index);
+  }
+
+  uint32_t StringIdsOffset() const { return string_ids_.GetOffset(); }
+  uint32_t TypeIdsOffset() const { return type_ids_.GetOffset(); }
+  uint32_t ProtoIdsOffset() const { return proto_ids_.GetOffset(); }
+  uint32_t FieldIdsOffset() const { return field_ids_.GetOffset(); }
+  uint32_t MethodIdsOffset() const { return method_ids_.GetOffset(); }
+  uint32_t ClassDefsOffset() const { return class_defs_.GetOffset(); }
+  uint32_t StringDatasOffset() const { return string_datas_.GetOffset(); }
+  uint32_t TypeListsOffset() const { return type_lists_.GetOffset(); }
+  uint32_t EncodedArrayOffset() const { return encoded_array_items_.GetOffset(); }
+  uint32_t AnnotationOffset() const { return annotation_items_.GetOffset(); }
+  uint32_t AnnotationSetOffset() const { return annotation_set_items_.GetOffset(); }
+  uint32_t AnnotationSetRefListsOffset() const { return annotation_set_ref_lists_.GetOffset(); }
+  uint32_t AnnotationsDirectoryOffset() const { return annotations_directory_items_.GetOffset(); }
+  uint32_t DebugInfoOffset() const { return debug_info_items_.GetOffset(); }
+  uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); }
+  uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); }
+
+  void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); }
+  void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); }
+  void SetProtoIdsOffset(uint32_t new_offset) { proto_ids_.SetOffset(new_offset); }
+  void SetFieldIdsOffset(uint32_t new_offset) { field_ids_.SetOffset(new_offset); }
+  void SetMethodIdsOffset(uint32_t new_offset) { method_ids_.SetOffset(new_offset); }
+  void SetClassDefsOffset(uint32_t new_offset) { class_defs_.SetOffset(new_offset); }
+  void SetStringDatasOffset(uint32_t new_offset) { string_datas_.SetOffset(new_offset); }
+  void SetTypeListsOffset(uint32_t new_offset) { type_lists_.SetOffset(new_offset); }
+  void SetEncodedArrayOffset(uint32_t new_offset) { encoded_array_items_.SetOffset(new_offset); }
+  void SetAnnotationOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); }
+  void SetAnnotationSetOffset(uint32_t new_offset) { annotation_set_items_.SetOffset(new_offset); }
+  void SetAnnotationSetRefListsOffset(uint32_t new_offset)
+      { annotation_set_ref_lists_.SetOffset(new_offset); }
+  void SetAnnotationsDirectoryOffset(uint32_t new_offset)
+      { annotations_directory_items_.SetOffset(new_offset); }
+  void SetDebugInfoOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); }
+  void SetCodeItemsOffset(uint32_t new_offset) { code_items_.SetOffset(new_offset); }
+  void SetClassDatasOffset(uint32_t new_offset) { class_datas_.SetOffset(new_offset); }
+
+  uint32_t StringIdsSize() const { return string_ids_.Size(); }
+  uint32_t TypeIdsSize() const { return type_ids_.Size(); }
+  uint32_t ProtoIdsSize() const { return proto_ids_.Size(); }
+  uint32_t FieldIdsSize() const { return field_ids_.Size(); }
+  uint32_t MethodIdsSize() const { return method_ids_.Size(); }
+  uint32_t ClassDefsSize() const { return class_defs_.Size(); }
+
+ private:
+  EncodedValue* ReadEncodedValue(const uint8_t** data);
+  EncodedValue* ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length);
+  void ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item);
+
+  ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id,
+      const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset);
+  MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii);
+
+  CollectionWithOffset<StringId> string_ids_;
+  CollectionWithOffset<TypeId> type_ids_;
+  CollectionWithOffset<ProtoId> proto_ids_;
+  CollectionWithOffset<FieldId> field_ids_;
+  CollectionWithOffset<MethodId> method_ids_;
+  CollectionWithOffset<ClassDef> class_defs_;
+
+  CollectionWithOffset<StringData> string_datas_;
+  CollectionWithOffset<TypeList> type_lists_;
+  CollectionWithOffset<EncodedArrayItem> encoded_array_items_;
+  CollectionWithOffset<AnnotationItem> annotation_items_;
+  CollectionWithOffset<AnnotationSetItem> annotation_set_items_;
+  CollectionWithOffset<AnnotationSetRefList> annotation_set_ref_lists_;
+  CollectionWithOffset<AnnotationsDirectoryItem> annotations_directory_items_;
+  CollectionWithOffset<DebugInfoItem> debug_info_items_;
+  CollectionWithOffset<CodeItem> code_items_;
+  CollectionWithOffset<ClassData> class_datas_;
+
+  DISALLOW_COPY_AND_ASSIGN(Collections);
+};
+
 class Item {
  public:
+  Item() { }
   virtual ~Item() { }
 
   uint32_t GetOffset() const { return offset_; }
+  uint32_t GetSize() const { return size_; }
   void SetOffset(uint32_t offset) { offset_ = offset; }
+  void SetSize(uint32_t size) { size_ = size; }
 
  protected:
+  Item(uint32_t offset, uint32_t size) : offset_(offset), size_(size) { }
+
   uint32_t offset_ = 0;
+  uint32_t size_ = 0;
+};
+
+class IndexedItem : public Item {
+ public:
+  IndexedItem() { }
+  virtual ~IndexedItem() { }
+
+  uint32_t GetIndex() const { return index_; }
+  void SetIndex(uint32_t index) { index_ = index; }
+
+ protected:
+  IndexedItem(uint32_t offset, uint32_t size, uint32_t index)
+      : Item(offset, size), index_(index) { }
+
+  uint32_t index_ = 0;
 };
 
 class Header : public Item {
@@ -124,7 +291,8 @@
          uint32_t link_offset,
          uint32_t data_size,
          uint32_t data_offset)
-      : checksum_(checksum),
+      : Item(0, kHeaderItemSize),
+        checksum_(checksum),
         endian_tag_(endian_tag),
         file_size_(file_size),
         header_size_(header_size),
@@ -137,6 +305,8 @@
   }
   ~Header() OVERRIDE { }
 
+  static size_t ItemSize() { return kHeaderItemSize; }
+
   const uint8_t* Magic() const { return magic_; }
   uint32_t Checksum() const { return checksum_; }
   const uint8_t* Signature() const { return signature_; }
@@ -159,39 +329,7 @@
   void SetDataSize(uint32_t new_data_size) { data_size_ = new_data_size; }
   void SetDataOffset(uint32_t new_data_offset) { data_offset_ = new_data_offset; }
 
-  // Collections.
-  std::vector<std::unique_ptr<StringId>>& StringIds() { return string_ids_.Collection(); }
-  std::vector<std::unique_ptr<TypeId>>& TypeIds() { return type_ids_.Collection(); }
-  std::vector<std::unique_ptr<ProtoId>>& ProtoIds() { return proto_ids_.Collection(); }
-  std::vector<std::unique_ptr<FieldId>>& FieldIds() { return field_ids_.Collection(); }
-  std::vector<std::unique_ptr<MethodId>>& MethodIds() { return method_ids_.Collection(); }
-  std::vector<std::unique_ptr<ClassDef>>& ClassDefs() { return class_defs_.Collection(); }
-  uint32_t StringIdsOffset() const { return string_ids_.GetOffset(); }
-  uint32_t TypeIdsOffset() const { return type_ids_.GetOffset(); }
-  uint32_t ProtoIdsOffset() const { return proto_ids_.GetOffset(); }
-  uint32_t FieldIdsOffset() const { return field_ids_.GetOffset(); }
-  uint32_t MethodIdsOffset() const { return method_ids_.GetOffset(); }
-  uint32_t ClassDefsOffset() const { return class_defs_.GetOffset(); }
-  void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); }
-  void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); }
-  void SetProtoIdsOffset(uint32_t new_offset) { proto_ids_.SetOffset(new_offset); }
-  void SetFieldIdsOffset(uint32_t new_offset) { field_ids_.SetOffset(new_offset); }
-  void SetMethodIdsOffset(uint32_t new_offset) { method_ids_.SetOffset(new_offset); }
-  void SetClassDefsOffset(uint32_t new_offset) { class_defs_.SetOffset(new_offset); }
-  uint32_t StringIdsSize() const { return string_ids_.Size(); }
-  uint32_t TypeIdsSize() const { return type_ids_.Size(); }
-  uint32_t ProtoIdsSize() const { return proto_ids_.Size(); }
-  uint32_t FieldIdsSize() const { return field_ids_.Size(); }
-  uint32_t MethodIdsSize() const { return method_ids_.Size(); }
-  uint32_t ClassDefsSize() const { return class_defs_.Size(); }
-
-  TypeId* GetTypeIdOrNullPtr(uint16_t index) {
-    return index == DexFile::kDexNoIndex16 ? nullptr : TypeIds()[index].get();
-  }
-
-  StringId* GetStringIdOrNullPtr(uint32_t index) {
-    return index == DexFile::kDexNoIndex ? nullptr : StringIds()[index].get();
-  }
+  Collections& GetCollections() { return collections_; }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
@@ -207,19 +345,16 @@
   uint32_t data_size_;
   uint32_t data_offset_;
 
-  CollectionWithOffset<StringId> string_ids_;
-  CollectionWithOffset<TypeId> type_ids_;
-  CollectionWithOffset<ProtoId> proto_ids_;
-  CollectionWithOffset<FieldId> field_ids_;
-  CollectionWithOffset<MethodId> method_ids_;
-  CollectionWithOffset<ClassDef> class_defs_;
+  Collections collections_;
+
   DISALLOW_COPY_AND_ASSIGN(Header);
 };
 
-class StringId : public Item {
+class StringData : public Item {
  public:
-  explicit StringId(const char* data) : data_(strdup(data)) { }
-  ~StringId() OVERRIDE { }
+  explicit StringData(const char* data) : data_(strdup(data)) {
+    size_ = UnsignedLeb128Size(strlen(data)) + strlen(data);
+  }
 
   const char* Data() const { return data_.get(); }
 
@@ -227,50 +362,95 @@
 
  private:
   std::unique_ptr<const char> data_;
+
+  DISALLOW_COPY_AND_ASSIGN(StringData);
+};
+
+class StringId : public IndexedItem {
+ public:
+  explicit StringId(StringData* string_data) : string_data_(string_data) {
+    size_ = kStringIdItemSize;
+  }
+  ~StringId() OVERRIDE { }
+
+  static size_t ItemSize() { return kStringIdItemSize; }
+
+  const char* Data() const { return string_data_->Data(); }
+  StringData* DataItem() const { return string_data_; }
+
+  void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
+
+ private:
+  StringData* string_data_;
+
   DISALLOW_COPY_AND_ASSIGN(StringId);
 };
 
-class TypeId : public Item {
+class TypeId : public IndexedItem {
  public:
-  explicit TypeId(StringId* string_id) : string_id_(string_id) { }
+  explicit TypeId(StringId* string_id) : string_id_(string_id) { size_ = kTypeIdItemSize; }
   ~TypeId() OVERRIDE { }
 
+  static size_t ItemSize() { return kTypeIdItemSize; }
+
   StringId* GetStringId() const { return string_id_; }
 
   void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
 
  private:
   StringId* string_id_;
+
   DISALLOW_COPY_AND_ASSIGN(TypeId);
 };
 
 using TypeIdVector = std::vector<const TypeId*>;
 
-class ProtoId : public Item {
+class TypeList : public Item {
  public:
-  ProtoId(const StringId* shorty, const TypeId* return_type, TypeIdVector* parameters)
-      : shorty_(shorty), return_type_(return_type), parameters_(parameters) { }
+  explicit TypeList(TypeIdVector* type_list) : type_list_(type_list) {
+    size_ = sizeof(uint32_t) + (type_list->size() * sizeof(uint16_t));
+  }
+  ~TypeList() OVERRIDE { }
+
+  const TypeIdVector* GetTypeList() const { return type_list_.get(); }
+
+ private:
+  std::unique_ptr<TypeIdVector> type_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(TypeList);
+};
+
+class ProtoId : public IndexedItem {
+ public:
+  ProtoId(const StringId* shorty, const TypeId* return_type, TypeList* parameters)
+      : shorty_(shorty), return_type_(return_type), parameters_(parameters)
+      { size_ = kProtoIdItemSize; }
   ~ProtoId() OVERRIDE { }
 
+  static size_t ItemSize() { return kProtoIdItemSize; }
+
   const StringId* Shorty() const { return shorty_; }
   const TypeId* ReturnType() const { return return_type_; }
-  const std::vector<const TypeId*>& Parameters() const { return *parameters_; }
+  const TypeIdVector& Parameters() const { return *parameters_->GetTypeList(); }
 
   void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
 
  private:
   const StringId* shorty_;
   const TypeId* return_type_;
-  std::unique_ptr<TypeIdVector> parameters_;
+  TypeList* parameters_;
+
   DISALLOW_COPY_AND_ASSIGN(ProtoId);
 };
 
-class FieldId : public Item {
+class FieldId : public IndexedItem {
  public:
   FieldId(const TypeId* klass, const TypeId* type, const StringId* name)
-      : class_(klass), type_(type), name_(name) { }
+      : class_(klass), type_(type), name_(name) { size_ = kFieldIdItemSize; }
   ~FieldId() OVERRIDE { }
 
+  static size_t ItemSize() { return kFieldIdItemSize; }
+
   const TypeId* Class() const { return class_; }
   const TypeId* Type() const { return type_; }
   const StringId* Name() const { return name_; }
@@ -281,15 +461,18 @@
   const TypeId* class_;
   const TypeId* type_;
   const StringId* name_;
+
   DISALLOW_COPY_AND_ASSIGN(FieldId);
 };
 
-class MethodId : public Item {
+class MethodId : public IndexedItem {
  public:
   MethodId(const TypeId* klass, const ProtoId* proto, const StringId* name)
-      : class_(klass), proto_(proto), name_(name) { }
+      : class_(klass), proto_(proto), name_(name) { size_ = kMethodIdItemSize; }
   ~MethodId() OVERRIDE { }
 
+  static size_t ItemSize() { return kMethodIdItemSize; }
+
   const TypeId* Class() const { return class_; }
   const ProtoId* Proto() const { return proto_; }
   const StringId* Name() const { return name_; }
@@ -300,6 +483,7 @@
   const TypeId* class_;
   const ProtoId* proto_;
   const StringId* name_;
+
   DISALLOW_COPY_AND_ASSIGN(MethodId);
 };
 
@@ -317,6 +501,7 @@
  private:
   uint32_t access_flags_;
   const FieldId* field_id_;
+
   DISALLOW_COPY_AND_ASSIGN(FieldItem);
 };
 
@@ -330,93 +515,126 @@
 
   uint32_t GetAccessFlags() const { return access_flags_; }
   const MethodId* GetMethodId() const { return method_id_; }
-  const CodeItem* GetCodeItem() const { return code_.get(); }
+  const CodeItem* GetCodeItem() const { return code_; }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
   uint32_t access_flags_;
   const MethodId* method_id_;
-  std::unique_ptr<const CodeItem> code_;
+  const CodeItem* code_;
+
   DISALLOW_COPY_AND_ASSIGN(MethodItem);
 };
 
 using MethodItemVector = std::vector<std::unique_ptr<MethodItem>>;
 
-class ArrayItem : public Item {
+class EncodedValue {
  public:
-  class NameValuePair {
-   public:
-    NameValuePair(StringId* name, ArrayItem* value)
-        : name_(name), value_(value) { }
-
-    StringId* Name() const { return name_; }
-    ArrayItem* Value() const { return value_.get(); }
-
-   private:
-    StringId* name_;
-    std::unique_ptr<ArrayItem> value_;
-    DISALLOW_COPY_AND_ASSIGN(NameValuePair);
-  };
-
-  struct ArrayItemVariant {
-   public:
-    union {
-      bool bool_val_;
-      int8_t byte_val_;
-      int16_t short_val_;
-      uint16_t char_val_;
-      int32_t int_val_;
-      int64_t long_val_;
-      float float_val_;
-      double double_val_;
-      StringId* string_val_;
-      FieldId* field_val_;
-      MethodId* method_val_;
-    } u_;
-    std::unique_ptr<std::vector<std::unique_ptr<ArrayItem>>> annotation_array_val_;
-    struct {
-      StringId* string_;
-      std::unique_ptr<std::vector<std::unique_ptr<NameValuePair>>> array_;
-    } annotation_annotation_val_;
-  };
-
-  explicit ArrayItem(uint8_t type) : type_(type) { }
-  ~ArrayItem() OVERRIDE { }
+  explicit EncodedValue(uint8_t type) : type_(type) { }
 
   int8_t Type() const { return type_; }
-  bool GetBoolean() const { return item_.u_.bool_val_; }
-  int8_t GetByte() const { return item_.u_.byte_val_; }
-  int16_t GetShort() const { return item_.u_.short_val_; }
-  uint16_t GetChar() const { return item_.u_.char_val_; }
-  int32_t GetInt() const { return item_.u_.int_val_; }
-  int64_t GetLong() const { return item_.u_.long_val_; }
-  float GetFloat() const { return item_.u_.float_val_; }
-  double GetDouble() const { return item_.u_.double_val_; }
-  StringId* GetStringId() const { return item_.u_.string_val_; }
-  FieldId* GetFieldId() const { return item_.u_.field_val_; }
-  MethodId* GetMethodId() const { return item_.u_.method_val_; }
-  std::vector<std::unique_ptr<ArrayItem>>* GetAnnotationArray() const {
-    return item_.annotation_array_val_.get();
-  }
-  StringId* GetAnnotationAnnotationString() const {
-    return item_.annotation_annotation_val_.string_;
-  }
-  std::vector<std::unique_ptr<NameValuePair>>* GetAnnotationAnnotationNameValuePairArray() const {
-    return item_.annotation_annotation_val_.array_.get();
-  }
-  // Used to construct the item union.  Ugly, but necessary.
-  ArrayItemVariant* GetArrayItemVariant() { return &item_; }
 
-  void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
+  void SetBoolean(bool z) { u_.bool_val_ = z; }
+  void SetByte(int8_t b) { u_.byte_val_ = b; }
+  void SetShort(int16_t s) { u_.short_val_ = s; }
+  void SetChar(uint16_t c) { u_.char_val_ = c; }
+  void SetInt(int32_t i) { u_.int_val_ = i; }
+  void SetLong(int64_t l) { u_.long_val_ = l; }
+  void SetFloat(float f) { u_.float_val_ = f; }
+  void SetDouble(double d) { u_.double_val_ = d; }
+  void SetStringId(StringId* string_id) { u_.string_val_ = string_id; }
+  void SetTypeId(TypeId* type_id) { u_.type_val_ = type_id; }
+  void SetFieldId(FieldId* field_id) { u_.field_val_ = field_id; }
+  void SetMethodId(MethodId* method_id) { u_.method_val_ = method_id; }
+  void SetEncodedArray(EncodedArrayItem* encoded_array) { encoded_array_.reset(encoded_array); }
+  void SetEncodedAnnotation(EncodedAnnotation* encoded_annotation)
+      { encoded_annotation_.reset(encoded_annotation); }
+
+  bool GetBoolean() const { return u_.bool_val_; }
+  int8_t GetByte() const { return u_.byte_val_; }
+  int16_t GetShort() const { return u_.short_val_; }
+  uint16_t GetChar() const { return u_.char_val_; }
+  int32_t GetInt() const { return u_.int_val_; }
+  int64_t GetLong() const { return u_.long_val_; }
+  float GetFloat() const { return u_.float_val_; }
+  double GetDouble() const { return u_.double_val_; }
+  StringId* GetStringId() const { return u_.string_val_; }
+  TypeId* GetTypeId() const { return u_.type_val_; }
+  FieldId* GetFieldId() const { return u_.field_val_; }
+  MethodId* GetMethodId() const { return u_.method_val_; }
+  EncodedArrayItem* GetEncodedArray() const { return encoded_array_.get(); }
+  EncodedAnnotation* GetEncodedAnnotation() const { return encoded_annotation_.get(); }
+
+  EncodedAnnotation* ReleaseEncodedAnnotation() { return encoded_annotation_.release(); }
 
  private:
   uint8_t type_;
-  ArrayItemVariant item_;
-  DISALLOW_COPY_AND_ASSIGN(ArrayItem);
+  union {
+    bool bool_val_;
+    int8_t byte_val_;
+    int16_t short_val_;
+    uint16_t char_val_;
+    int32_t int_val_;
+    int64_t long_val_;
+    float float_val_;
+    double double_val_;
+    StringId* string_val_;
+    TypeId* type_val_;
+    FieldId* field_val_;
+    MethodId* method_val_;
+  } u_;
+  std::unique_ptr<EncodedArrayItem> encoded_array_;
+  std::unique_ptr<EncodedAnnotation> encoded_annotation_;
+
+  DISALLOW_COPY_AND_ASSIGN(EncodedValue);
 };
 
-using ArrayItemVector = std::vector<std::unique_ptr<ArrayItem>>;
+using EncodedValueVector = std::vector<std::unique_ptr<EncodedValue>>;
+
+class AnnotationElement {
+ public:
+  AnnotationElement(StringId* name, EncodedValue* value) : name_(name), value_(value) { }
+
+  StringId* GetName() const { return name_; }
+  EncodedValue* GetValue() const { return value_.get(); }
+
+ private:
+  StringId* name_;
+  std::unique_ptr<EncodedValue> value_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnnotationElement);
+};
+
+using AnnotationElementVector = std::vector<std::unique_ptr<AnnotationElement>>;
+
+class EncodedAnnotation {
+ public:
+  EncodedAnnotation(TypeId* type, AnnotationElementVector* elements)
+      : type_(type), elements_(elements) { }
+
+  TypeId* GetType() const { return type_; }
+  AnnotationElementVector* GetAnnotationElements() const { return elements_.get(); }
+
+ private:
+  TypeId* type_;
+  std::unique_ptr<AnnotationElementVector> elements_;
+
+  DISALLOW_COPY_AND_ASSIGN(EncodedAnnotation);
+};
+
+class EncodedArrayItem : public Item {
+ public:
+  explicit EncodedArrayItem(EncodedValueVector* encoded_values)
+      : encoded_values_(encoded_values) { }
+
+  EncodedValueVector* GetEncodedValues() const { return encoded_values_.get(); }
+
+ private:
+  std::unique_ptr<EncodedValueVector> encoded_values_;
+
+  DISALLOW_COPY_AND_ASSIGN(EncodedArrayItem);
+};
 
 class ClassData : public Item {
  public:
@@ -442,42 +660,43 @@
   std::unique_ptr<FieldItemVector> instance_fields_;
   std::unique_ptr<MethodItemVector> direct_methods_;
   std::unique_ptr<MethodItemVector> virtual_methods_;
+
   DISALLOW_COPY_AND_ASSIGN(ClassData);
 };
 
-class ClassDef : public Item {
+class ClassDef : public IndexedItem {
  public:
   ClassDef(const TypeId* class_type,
            uint32_t access_flags,
            const TypeId* superclass,
-           TypeIdVector* interfaces,
-           uint32_t interfaces_offset,
+           TypeList* interfaces,
            const StringId* source_file,
            AnnotationsDirectoryItem* annotations,
-           ArrayItemVector* static_values,
+           EncodedArrayItem* static_values,
            ClassData* class_data)
       : class_type_(class_type),
         access_flags_(access_flags),
         superclass_(superclass),
         interfaces_(interfaces),
-        interfaces_offset_(interfaces_offset),
         source_file_(source_file),
         annotations_(annotations),
         static_values_(static_values),
-        class_data_(class_data) { }
+        class_data_(class_data) { size_ = kClassDefItemSize; }
 
   ~ClassDef() OVERRIDE { }
 
+  static size_t ItemSize() { return kClassDefItemSize; }
+
   const TypeId* ClassType() const { return class_type_; }
   uint32_t GetAccessFlags() const { return access_flags_; }
   const TypeId* Superclass() const { return superclass_; }
-  TypeIdVector* Interfaces() { return interfaces_.get(); }
-  uint32_t InterfacesOffset() const { return interfaces_offset_; }
-  void SetInterfacesOffset(uint32_t new_offset) { interfaces_offset_ = new_offset; }
+  const TypeIdVector* Interfaces()
+      { return interfaces_ == nullptr ? nullptr: interfaces_->GetTypeList(); }
+  uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); }
   const StringId* SourceFile() const { return source_file_; }
-  AnnotationsDirectoryItem* Annotations() const { return annotations_.get(); }
-  ArrayItemVector* StaticValues() { return static_values_.get(); }
-  ClassData* GetClassData() { return class_data_.get(); }
+  AnnotationsDirectoryItem* Annotations() const { return annotations_; }
+  EncodedArrayItem* StaticValues() { return static_values_; }
+  ClassData* GetClassData() { return class_data_; }
 
   MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii);
 
@@ -487,12 +706,12 @@
   const TypeId* class_type_;
   uint32_t access_flags_;
   const TypeId* superclass_;
-  std::unique_ptr<TypeIdVector> interfaces_;
-  uint32_t interfaces_offset_;
+  TypeList* interfaces_;
   const StringId* source_file_;
-  std::unique_ptr<AnnotationsDirectoryItem> annotations_;
-  std::unique_ptr<ArrayItemVector> static_values_;
-  std::unique_ptr<ClassData> class_data_;
+  AnnotationsDirectoryItem* annotations_;
+  EncodedArrayItem* static_values_;
+  ClassData* class_data_;
+
   DISALLOW_COPY_AND_ASSIGN(ClassDef);
 };
 
@@ -506,6 +725,7 @@
  private:
   const TypeId* type_id_;
   uint32_t address_;
+
   DISALLOW_COPY_AND_ASSIGN(CatchHandler);
 };
 
@@ -527,6 +747,7 @@
   uint32_t start_addr_;
   uint16_t insn_count_;
   std::unique_ptr<CatchHandlerVector> handlers_;
+
   DISALLOW_COPY_AND_ASSIGN(TryItem);
 };
 
@@ -555,7 +776,7 @@
   uint16_t InsSize() const { return ins_size_; }
   uint16_t OutsSize() const { return outs_size_; }
   uint16_t TriesSize() const { return tries_ == nullptr ? 0 : tries_->size(); }
-  DebugInfoItem* DebugInfo() const { return debug_info_.get(); }
+  DebugInfoItem* DebugInfo() const { return debug_info_; }
   uint32_t InsnsSize() const { return insns_size_; }
   uint16_t* Insns() const { return insns_.get(); }
   TryItemVector* Tries() const { return tries_.get(); }
@@ -566,14 +787,14 @@
   uint16_t registers_size_;
   uint16_t ins_size_;
   uint16_t outs_size_;
-  std::unique_ptr<DebugInfoItem> debug_info_;
+  DebugInfoItem* debug_info_;
   uint32_t insns_size_;
   std::unique_ptr<uint16_t[]> insns_;
   std::unique_ptr<TryItemVector> tries_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeItem);
 };
 
-
 struct PositionInfo {
   PositionInfo(uint32_t address, uint32_t line) : address_(address), line_(line) { }
 
@@ -617,39 +838,60 @@
  private:
   PositionInfoVector positions_;
   LocalInfoVector locals_;
+
   DISALLOW_COPY_AND_ASSIGN(DebugInfoItem);
 };
 
-class AnnotationItem {
+class AnnotationItem : public Item {
  public:
-  AnnotationItem(uint8_t visibility, ArrayItem* item) : visibility_(visibility), item_(item) { }
+  AnnotationItem(uint8_t visibility, EncodedAnnotation* annotation)
+      : visibility_(visibility), annotation_(annotation) { }
 
   uint8_t GetVisibility() const { return visibility_; }
-  ArrayItem* GetItem() const { return item_.get(); }
-
- private:
-  uint8_t visibility_;
-  std::unique_ptr<ArrayItem> item_;
-  DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
-};
-
-using AnnotationItemVector = std::vector<std::unique_ptr<AnnotationItem>>;
-
-class AnnotationSetItem : public Item {
- public:
-  explicit AnnotationSetItem(AnnotationItemVector* items) : items_(items) { }
-  ~AnnotationSetItem() OVERRIDE { }
-
-  AnnotationItemVector* GetItems() { return items_.get(); }
+  EncodedAnnotation* GetAnnotation() const { return annotation_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  std::unique_ptr<AnnotationItemVector> items_;
+  uint8_t visibility_;
+  std::unique_ptr<EncodedAnnotation> annotation_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
+};
+
+class AnnotationSetItem : public Item {
+ public:
+  explicit AnnotationSetItem(std::vector<AnnotationItem*>* items) : items_(items) {
+    size_ = sizeof(uint32_t) + items->size() * sizeof(uint32_t);
+  }
+  ~AnnotationSetItem() OVERRIDE { }
+
+  std::vector<AnnotationItem*>* GetItems() { return items_.get(); }
+
+  void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
+
+ private:
+  std::unique_ptr<std::vector<AnnotationItem*>> items_;
+
   DISALLOW_COPY_AND_ASSIGN(AnnotationSetItem);
 };
 
-using AnnotationSetItemVector = std::vector<std::unique_ptr<AnnotationSetItem>>;
+class AnnotationSetRefList : public Item {
+ public:
+  explicit AnnotationSetRefList(std::vector<AnnotationSetItem*>* items) : items_(items) {
+    size_ = sizeof(uint32_t) + items->size() * sizeof(uint32_t);
+  }
+  ~AnnotationSetRefList() OVERRIDE { }
+
+  std::vector<AnnotationSetItem*>* GetItems() { return items_.get(); }
+
+  void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
+
+ private:
+  std::unique_ptr<std::vector<AnnotationSetItem*>> items_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList);
+};
 
 class FieldAnnotation {
  public:
@@ -657,11 +899,12 @@
       : field_id_(field_id), annotation_set_item_(annotation_set_item) { }
 
   FieldId* GetFieldId() const { return field_id_; }
-  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
+  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_; }
 
  private:
   FieldId* field_id_;
-  std::unique_ptr<AnnotationSetItem> annotation_set_item_;
+  AnnotationSetItem* annotation_set_item_;
+
   DISALLOW_COPY_AND_ASSIGN(FieldAnnotation);
 };
 
@@ -673,11 +916,12 @@
       : method_id_(method_id), annotation_set_item_(annotation_set_item) { }
 
   MethodId* GetMethodId() const { return method_id_; }
-  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
+  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_; }
 
  private:
   MethodId* method_id_;
-  std::unique_ptr<AnnotationSetItem> annotation_set_item_;
+  AnnotationSetItem* annotation_set_item_;
+
   DISALLOW_COPY_AND_ASSIGN(MethodAnnotation);
 };
 
@@ -685,15 +929,16 @@
 
 class ParameterAnnotation {
  public:
-  ParameterAnnotation(MethodId* method_id, AnnotationSetItemVector* annotations)
+  ParameterAnnotation(MethodId* method_id, AnnotationSetRefList* annotations)
       : method_id_(method_id), annotations_(annotations) { }
 
   MethodId* GetMethodId() const { return method_id_; }
-  AnnotationSetItemVector* GetAnnotations() { return annotations_.get(); }
+  AnnotationSetRefList* GetAnnotations() { return annotations_; }
 
  private:
   MethodId* method_id_;
-  std::unique_ptr<AnnotationSetItemVector> annotations_;
+  AnnotationSetRefList* annotations_;
+
   DISALLOW_COPY_AND_ASSIGN(ParameterAnnotation);
 };
 
@@ -710,7 +955,7 @@
         method_annotations_(method_annotations),
         parameter_annotations_(parameter_annotations) { }
 
-  AnnotationSetItem* GetClassAnnotation() const { return class_annotation_.get(); }
+  AnnotationSetItem* GetClassAnnotation() const { return class_annotation_; }
   FieldAnnotationVector* GetFieldAnnotations() { return field_annotations_.get(); }
   MethodAnnotationVector* GetMethodAnnotations() { return method_annotations_.get(); }
   ParameterAnnotationVector* GetParameterAnnotations() { return parameter_annotations_.get(); }
@@ -718,10 +963,11 @@
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  std::unique_ptr<AnnotationSetItem> class_annotation_;
+  AnnotationSetItem* class_annotation_;
   std::unique_ptr<FieldAnnotationVector> field_annotations_;
   std::unique_ptr<MethodAnnotationVector> method_annotations_;
   std::unique_ptr<ParameterAnnotationVector> parameter_annotations_;
+
   DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem);
 };
 
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 30f57d9..e6868d7 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -24,401 +24,6 @@
 namespace art {
 namespace dex_ir {
 
-namespace {
-
-static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) {
-  uint64_t value = 0;
-  for (uint32_t i = 0; i <= length; i++) {
-    value |= static_cast<uint64_t>(*(*data)++) << (i * 8);
-  }
-  if (sign_extend) {
-    int shift = (7 - length) * 8;
-    return (static_cast<int64_t>(value) << shift) >> shift;
-  }
-  return value;
-}
-
-// Prototype to break cyclic dependency.
-void ReadArrayItemVariant(Header& header,
-                          const uint8_t** data,
-                          uint8_t type,
-                          uint8_t length,
-                          ArrayItem::ArrayItemVariant* item);
-
-ArrayItem* ReadArrayItem(Header& header, const uint8_t** data, uint8_t type, uint8_t length) {
-  ArrayItem* item = new ArrayItem(type);
-  ReadArrayItemVariant(header, data, type, length, item->GetArrayItemVariant());
-  return item;
-}
-
-ArrayItem* ReadArrayItem(Header& header, const uint8_t** data) {
-  const uint8_t encoded_value = *(*data)++;
-  const uint8_t type = encoded_value & 0x1f;
-  ArrayItem* item = new ArrayItem(type);
-  ReadArrayItemVariant(header, data, type, encoded_value >> 5, item->GetArrayItemVariant());
-  return item;
-}
-
-void ReadArrayItemVariant(Header& header,
-                          const uint8_t** data,
-                          uint8_t type,
-                          uint8_t length,
-                          ArrayItem::ArrayItemVariant* item) {
-  switch (type) {
-    case DexFile::kDexAnnotationByte:
-      item->u_.byte_val_ = static_cast<int8_t>(ReadVarWidth(data, length, false));
-      break;
-    case DexFile::kDexAnnotationShort:
-      item->u_.short_val_ = static_cast<int16_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationChar:
-      item->u_.char_val_ = static_cast<uint16_t>(ReadVarWidth(data, length, false));
-      break;
-    case DexFile::kDexAnnotationInt:
-      item->u_.int_val_ = static_cast<int32_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationLong:
-      item->u_.long_val_ = static_cast<int64_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationFloat: {
-      // Fill on right.
-      union {
-        float f;
-        uint32_t data;
-      } conv;
-      conv.data = static_cast<uint32_t>(ReadVarWidth(data, length, false)) << (3 - length) * 8;
-      item->u_.float_val_ = conv.f;
-      break;
-    }
-    case DexFile::kDexAnnotationDouble: {
-      // Fill on right.
-      union {
-        double d;
-        uint64_t data;
-      } conv;
-      conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8;
-      item->u_.double_val_ = conv.d;
-      break;
-    }
-    case DexFile::kDexAnnotationString: {
-      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item->u_.string_val_ = header.StringIds()[string_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationType: {
-      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item->u_.string_val_ = header.TypeIds()[string_index]->GetStringId();
-      break;
-    }
-    case DexFile::kDexAnnotationField:
-    case DexFile::kDexAnnotationEnum: {
-      const uint32_t field_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item->u_.field_val_ = header.FieldIds()[field_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationMethod: {
-      const uint32_t method_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item->u_.method_val_ = header.MethodIds()[method_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationArray: {
-      item->annotation_array_val_.reset(new ArrayItemVector());
-      // Decode all elements.
-      const uint32_t size = DecodeUnsignedLeb128(data);
-      for (uint32_t i = 0; i < size; i++) {
-        item->annotation_array_val_->push_back(
-            std::unique_ptr<ArrayItem>(ReadArrayItem(header, data)));
-      }
-      break;
-    }
-    case DexFile::kDexAnnotationAnnotation: {
-      const uint32_t type_idx = DecodeUnsignedLeb128(data);
-      item->annotation_annotation_val_.string_ = header.TypeIds()[type_idx]->GetStringId();
-      item->annotation_annotation_val_.array_.reset(
-          new std::vector<std::unique_ptr<ArrayItem::NameValuePair>>());
-      // Decode all name=value pairs.
-      const uint32_t size = DecodeUnsignedLeb128(data);
-      for (uint32_t i = 0; i < size; i++) {
-        const uint32_t name_index = DecodeUnsignedLeb128(data);
-        item->annotation_annotation_val_.array_->push_back(
-            std::unique_ptr<ArrayItem::NameValuePair>(
-                new ArrayItem::NameValuePair(header.StringIds()[name_index].get(),
-                                             ReadArrayItem(header, data))));
-      }
-      break;
-    }
-    case DexFile::kDexAnnotationNull:
-      break;
-    case DexFile::kDexAnnotationBoolean:
-      item->u_.bool_val_ = (length != 0);
-      break;
-    default:
-      break;
-  }
-}
-
-static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  PositionInfoVector& positions = debug_info->GetPositionInfo();
-  positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
-  return false;
-}
-
-static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  LocalInfoVector& locals = debug_info->GetLocalInfo();
-  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
-  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
-  locals.push_back(std::unique_ptr<LocalInfo>(
-      new LocalInfo(name, entry.descriptor_, signature, entry.start_address_,
-                    entry.end_address_, entry.reg_)));
-}
-
-CodeItem* ReadCodeItem(const DexFile& dex_file,
-                       const DexFile::CodeItem& disk_code_item,
-                       Header& header) {
-  uint16_t registers_size = disk_code_item.registers_size_;
-  uint16_t ins_size = disk_code_item.ins_size_;
-  uint16_t outs_size = disk_code_item.outs_size_;
-  uint32_t tries_size = disk_code_item.tries_size_;
-
-  const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
-  DebugInfoItem* debug_info = nullptr;
-  if (debug_info_stream != nullptr) {
-    debug_info = new DebugInfoItem();
-  }
-
-  uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
-  uint16_t* insns = new uint16_t[insns_size];
-  memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
-
-  TryItemVector* tries = nullptr;
-  if (tries_size > 0) {
-    tries = new TryItemVector();
-    for (uint32_t i = 0; i < tries_size; ++i) {
-      const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
-      uint32_t start_addr = disk_try_item->start_addr_;
-      uint16_t insn_count = disk_try_item->insn_count_;
-      CatchHandlerVector* handlers = new CatchHandlerVector();
-      for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
-        const uint16_t type_index = it.GetHandlerTypeIndex();
-        const TypeId* type_id = header.GetTypeIdOrNullPtr(type_index);
-        handlers->push_back(std::unique_ptr<const CatchHandler>(
-            new CatchHandler(type_id, it.GetHandlerAddress())));
-      }
-      TryItem* try_item = new TryItem(start_addr, insn_count, handlers);
-      tries->push_back(std::unique_ptr<const TryItem>(try_item));
-    }
-  }
-  return new CodeItem(registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries);
-}
-
-MethodItem* GenerateMethodItem(const DexFile& dex_file,
-                               dex_ir::Header& header,
-                               ClassDataItemIterator& cdii) {
-  MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
-  uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-  const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
-  CodeItem* code_item = nullptr;
-  DebugInfoItem* debug_info = nullptr;
-  if (disk_code_item != nullptr) {
-    code_item = ReadCodeItem(dex_file, *disk_code_item, header);
-    code_item->SetOffset(cdii.GetMethodCodeItemOffset());
-    debug_info = code_item->DebugInfo();
-  }
-  if (debug_info != nullptr) {
-    bool is_static = (access_flags & kAccStatic) != 0;
-    dex_file.DecodeDebugLocalInfo(
-        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
-    dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
-  }
-  return new MethodItem(access_flags, method_item, code_item);
-}
-
-AnnotationSetItem* ReadAnnotationSetItem(const DexFile& dex_file,
-                                         const DexFile::AnnotationSetItem& disk_annotations_item,
-                                         Header& header) {
-  if (disk_annotations_item.size_ == 0) {
-    return nullptr;
-  }
-  AnnotationItemVector* items = new AnnotationItemVector();
-  for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
-    const DexFile::AnnotationItem* annotation =
-        dex_file.GetAnnotationItem(&disk_annotations_item, i);
-    if (annotation == nullptr) {
-      continue;
-    }
-    uint8_t visibility = annotation->visibility_;
-    const uint8_t* annotation_data = annotation->annotation_;
-    ArrayItem* array_item =
-        ReadArrayItem(header, &annotation_data, DexFile::kDexAnnotationAnnotation, 0);
-    items->push_back(std::unique_ptr<AnnotationItem>(new AnnotationItem(visibility, array_item)));
-  }
-  return new AnnotationSetItem(items);
-}
-
-ParameterAnnotation* ReadParameterAnnotation(
-    const DexFile& dex_file,
-    MethodId* method_id,
-    const DexFile::AnnotationSetRefList* annotation_set_ref_list,
-    Header& header) {
-  AnnotationSetItemVector* annotations = new AnnotationSetItemVector();
-  for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
-    const DexFile::AnnotationSetItem* annotation_set_item =
-        dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
-    annotations->push_back(std::unique_ptr<AnnotationSetItem>(
-        ReadAnnotationSetItem(dex_file, *annotation_set_item, header)));
-  }
-  return new ParameterAnnotation(method_id, annotations);
-}
-
-AnnotationsDirectoryItem* ReadAnnotationsDirectoryItem(
-    const DexFile& dex_file,
-    const DexFile::AnnotationsDirectoryItem* disk_annotations_item,
-    Header& header) {
-  const DexFile::AnnotationSetItem* class_set_item =
-      dex_file.GetClassAnnotationSet(disk_annotations_item);
-  AnnotationSetItem* class_annotation = nullptr;
-  if (class_set_item != nullptr) {
-    class_annotation = ReadAnnotationSetItem(dex_file, *class_set_item, header);
-  }
-  const DexFile::FieldAnnotationsItem* fields =
-      dex_file.GetFieldAnnotations(disk_annotations_item);
-  FieldAnnotationVector* field_annotations = nullptr;
-  if (fields != nullptr) {
-    field_annotations = new FieldAnnotationVector();
-    for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) {
-      FieldId* field_id = header.FieldIds()[fields[i].field_idx_].get();
-      const DexFile::AnnotationSetItem* field_set_item =
-          dex_file.GetFieldAnnotationSetItem(fields[i]);
-      AnnotationSetItem* annotation_set_item =
-          ReadAnnotationSetItem(dex_file, *field_set_item, header);
-      field_annotations->push_back(std::unique_ptr<FieldAnnotation>(
-          new FieldAnnotation(field_id, annotation_set_item)));
-    }
-  }
-  const DexFile::MethodAnnotationsItem* methods =
-      dex_file.GetMethodAnnotations(disk_annotations_item);
-  MethodAnnotationVector* method_annotations = nullptr;
-  if (methods != nullptr) {
-    method_annotations = new MethodAnnotationVector();
-    for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) {
-      MethodId* method_id = header.MethodIds()[methods[i].method_idx_].get();
-      const DexFile::AnnotationSetItem* method_set_item =
-          dex_file.GetMethodAnnotationSetItem(methods[i]);
-      AnnotationSetItem* annotation_set_item =
-          ReadAnnotationSetItem(dex_file, *method_set_item, header);
-      method_annotations->push_back(std::unique_ptr<MethodAnnotation>(
-          new MethodAnnotation(method_id, annotation_set_item)));
-    }
-  }
-  const DexFile::ParameterAnnotationsItem* parameters =
-      dex_file.GetParameterAnnotations(disk_annotations_item);
-  ParameterAnnotationVector* parameter_annotations = nullptr;
-  if (parameters != nullptr) {
-    parameter_annotations = new ParameterAnnotationVector();
-    for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) {
-      MethodId* method_id = header.MethodIds()[parameters[i].method_idx_].get();
-      const DexFile::AnnotationSetRefList* list =
-          dex_file.GetParameterAnnotationSetRefList(&parameters[i]);
-      parameter_annotations->push_back(std::unique_ptr<ParameterAnnotation>(
-          ReadParameterAnnotation(dex_file, method_id, list, header)));
-    }
-  }
-
-  return new AnnotationsDirectoryItem(class_annotation,
-                                      field_annotations,
-                                      method_annotations,
-                                      parameter_annotations);
-}
-
-ClassDef* ReadClassDef(const DexFile& dex_file,
-                       const DexFile::ClassDef& disk_class_def,
-                       Header& header) {
-  const TypeId* class_type = header.TypeIds()[disk_class_def.class_idx_].get();
-  uint32_t access_flags = disk_class_def.access_flags_;
-  const TypeId* superclass = header.GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
-
-  TypeIdVector* interfaces = nullptr;
-  const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
-  uint32_t interfaces_offset = disk_class_def.interfaces_off_;
-  if (type_list != nullptr) {
-    interfaces = new TypeIdVector();
-    for (uint32_t index = 0; index < type_list->Size(); ++index) {
-      interfaces->push_back(header.TypeIds()[type_list->GetTypeItem(index).type_idx_].get());
-    }
-  }
-  const StringId* source_file = header.GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
-  // Annotations.
-  AnnotationsDirectoryItem* annotations = nullptr;
-  const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
-      dex_file.GetAnnotationsDirectory(disk_class_def);
-  if (disk_annotations_directory_item != nullptr) {
-    annotations = ReadAnnotationsDirectoryItem(dex_file, disk_annotations_directory_item, header);
-    annotations->SetOffset(disk_class_def.annotations_off_);
-  }
-  // Static field initializers.
-  ArrayItemVector* static_values = nullptr;
-  const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def);
-  if (static_data != nullptr) {
-    uint32_t static_value_count = static_data == nullptr ? 0 : DecodeUnsignedLeb128(&static_data);
-    if (static_value_count > 0) {
-      static_values = new ArrayItemVector();
-      for (uint32_t i = 0; i < static_value_count; ++i) {
-        static_values->push_back(std::unique_ptr<ArrayItem>(ReadArrayItem(header, &static_data)));
-      }
-    }
-  }
-  // Read the fields and methods defined by the class, resolving the circular reference from those
-  // to classes by setting class at the same time.
-  const uint8_t* encoded_data = dex_file.GetClassData(disk_class_def);
-  ClassData* class_data = nullptr;
-  if (encoded_data != nullptr) {
-    uint32_t offset = disk_class_def.class_data_off_;
-    ClassDataItemIterator cdii(dex_file, encoded_data);
-    // Static fields.
-    FieldItemVector* static_fields = new FieldItemVector();
-    for (uint32_t i = 0; cdii.HasNextStaticField(); i++, cdii.Next()) {
-      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
-    }
-    // Instance fields.
-    FieldItemVector* instance_fields = new FieldItemVector();
-    for (uint32_t i = 0; cdii.HasNextInstanceField(); i++, cdii.Next()) {
-      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      instance_fields->push_back(
-          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
-    }
-    // Direct methods.
-    MethodItemVector* direct_methods = new MethodItemVector();
-    for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
-      direct_methods->push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, header, cdii)));
-    }
-    // Virtual methods.
-    MethodItemVector* virtual_methods = new MethodItemVector();
-    for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
-      virtual_methods->push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, header, cdii)));
-    }
-    class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
-    class_data->SetOffset(offset);
-  }
-  return new ClassDef(class_type,
-                      access_flags,
-                      superclass,
-                      interfaces,
-                      interfaces_offset,
-                      source_file,
-                      annotations,
-                      static_values,
-                      class_data);
-}
-
-}  // namespace
-
 Header* DexIrBuilder(const DexFile& dex_file) {
   const DexFile::Header& disk_header = dex_file.GetHeader();
   Header* header = new Header(disk_header.magic_,
@@ -431,73 +36,37 @@
                               disk_header.link_off_,
                               disk_header.data_size_,
                               disk_header.data_off_);
+  Collections& collections = header->GetCollections();
   // Walk the rest of the header fields.
   // StringId table.
-  std::vector<std::unique_ptr<StringId>>& string_ids = header->StringIds();
-  header->SetStringIdsOffset(disk_header.string_ids_off_);
+  collections.SetStringIdsOffset(disk_header.string_ids_off_);
   for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) {
-    const DexFile::StringId& disk_string_id = dex_file.GetStringId(i);
-    StringId* string_id = new StringId(dex_file.GetStringData(disk_string_id));
-    string_id->SetOffset(i);
-    string_ids.push_back(std::unique_ptr<StringId>(string_id));
+    collections.CreateStringId(dex_file, i);
   }
   // TypeId table.
-  std::vector<std::unique_ptr<TypeId>>& type_ids = header->TypeIds();
-  header->SetTypeIdsOffset(disk_header.type_ids_off_);
+  collections.SetTypeIdsOffset(disk_header.type_ids_off_);
   for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
-    const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(i);
-    TypeId* type_id = new TypeId(header->StringIds()[disk_type_id.descriptor_idx_].get());
-    type_id->SetOffset(i);
-    type_ids.push_back(std::unique_ptr<TypeId>(type_id));
+    collections.CreateTypeId(dex_file, i);
   }
   // ProtoId table.
-  std::vector<std::unique_ptr<ProtoId>>& proto_ids = header->ProtoIds();
-  header->SetProtoIdsOffset(disk_header.proto_ids_off_);
+  collections.SetProtoIdsOffset(disk_header.proto_ids_off_);
   for (uint32_t i = 0; i < dex_file.NumProtoIds(); ++i) {
-    const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
-    // Build the parameter type vector.
-    TypeIdVector* parameters = new TypeIdVector();
-    DexFileParameterIterator dfpi(dex_file, disk_proto_id);
-    while (dfpi.HasNext()) {
-      parameters->push_back(header->TypeIds()[dfpi.GetTypeIdx()].get());
-      dfpi.Next();
-    }
-    ProtoId* proto_id = new ProtoId(header->StringIds()[disk_proto_id.shorty_idx_].get(),
-                                    header->TypeIds()[disk_proto_id.return_type_idx_].get(),
-                                    parameters);
-    proto_id->SetOffset(i);
-    proto_ids.push_back(std::unique_ptr<ProtoId>(proto_id));
+    collections.CreateProtoId(dex_file, i);
   }
   // FieldId table.
-  std::vector<std::unique_ptr<FieldId>>& field_ids = header->FieldIds();
-  header->SetFieldIdsOffset(disk_header.field_ids_off_);
+  collections.SetFieldIdsOffset(disk_header.field_ids_off_);
   for (uint32_t i = 0; i < dex_file.NumFieldIds(); ++i) {
-    const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
-    FieldId* field_id = new FieldId(header->TypeIds()[disk_field_id.class_idx_].get(),
-                                    header->TypeIds()[disk_field_id.type_idx_].get(),
-                                    header->StringIds()[disk_field_id.name_idx_].get());
-    field_id->SetOffset(i);
-    field_ids.push_back(std::unique_ptr<FieldId>(field_id));
+    collections.CreateFieldId(dex_file, i);
   }
   // MethodId table.
-  std::vector<std::unique_ptr<MethodId>>& method_ids = header->MethodIds();
-  header->SetMethodIdsOffset(disk_header.method_ids_off_);
+  collections.SetMethodIdsOffset(disk_header.method_ids_off_);
   for (uint32_t i = 0; i < dex_file.NumMethodIds(); ++i) {
-    const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
-    MethodId* method_id = new MethodId(header->TypeIds()[disk_method_id.class_idx_].get(),
-                                       header->ProtoIds()[disk_method_id.proto_idx_].get(),
-                                       header->StringIds()[disk_method_id.name_idx_].get());
-    method_id->SetOffset(i);
-    method_ids.push_back(std::unique_ptr<MethodId>(method_id));
+    collections.CreateMethodId(dex_file, i);
   }
   // ClassDef table.
-  std::vector<std::unique_ptr<ClassDef>>& class_defs = header->ClassDefs();
-  header->SetClassDefsOffset(disk_header.class_defs_off_);
+  collections.SetClassDefsOffset(disk_header.class_defs_off_);
   for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
-    const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i);
-    ClassDef* class_def = ReadClassDef(dex_file, disk_class_def, *header);
-    class_def->SetOffset(i);
-    class_defs.push_back(std::unique_ptr<ClassDef>(class_def));
+    collections.CreateClassDef(dex_file, i);
   }
 
   return header;
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 3a3f417..6f34a33 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,9 +30,11 @@
 #include <sstream>
 #include <vector>
 
+#include "base/unix_file/fd_file.h"
 #include "dex_ir_builder.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
+#include "os.h"
 #include "utils.h"
 
 namespace art {
@@ -348,10 +350,26 @@
   }  // for
 }
 
+// Forward declare to resolve circular dependence.
+static void DumpEncodedValue(const dex_ir::EncodedValue* data);
+
+/*
+ * Dumps encoded annotation.
+ */
+static void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation) {
+  fputs(annotation->GetType()->GetStringId()->Data(), out_file_);
+  // Display all name=value pairs.
+  for (auto& subannotation : *annotation->GetAnnotationElements()) {
+    fputc(' ', out_file_);
+    fputs(subannotation->GetName()->Data(), out_file_);
+    fputc('=', out_file_);
+    DumpEncodedValue(subannotation->GetValue());
+  }
+}
 /*
  * Dumps encoded value.
  */
-static void DumpEncodedValue(const dex_ir::ArrayItem* data) {
+static void DumpEncodedValue(const dex_ir::EncodedValue* data) {
   switch (data->Type()) {
     case DexFile::kDexAnnotationByte:
       fprintf(out_file_, "%" PRId8, data->GetByte());
@@ -386,8 +404,8 @@
       break;
     }
     case DexFile::kDexAnnotationType: {
-      dex_ir::StringId* string_id = data->GetStringId();
-      fputs(string_id->Data(), out_file_);
+      dex_ir::TypeId* type_id = data->GetTypeId();
+      fputs(type_id->GetStringId()->Data(), out_file_);
       break;
     }
     case DexFile::kDexAnnotationField:
@@ -404,22 +422,15 @@
     case DexFile::kDexAnnotationArray: {
       fputc('{', out_file_);
       // Display all elements.
-      for (auto& array : *data->GetAnnotationArray()) {
+      for (auto& value : *data->GetEncodedArray()->GetEncodedValues()) {
         fputc(' ', out_file_);
-        DumpEncodedValue(array.get());
+        DumpEncodedValue(value.get());
       }
       fputs(" }", out_file_);
       break;
     }
     case DexFile::kDexAnnotationAnnotation: {
-      fputs(data->GetAnnotationAnnotationString()->Data(), out_file_);
-      // Display all name=value pairs.
-      for (auto& subannotation : *data->GetAnnotationAnnotationNameValuePairArray()) {
-        fputc(' ', out_file_);
-        fputs(subannotation->Name()->Data(), out_file_);
-        fputc('=', out_file_);
-        DumpEncodedValue(subannotation->Value());
-      }
+      DumpEncodedAnnotation(data->GetEncodedAnnotation());
       break;
     }
     case DexFile::kDexAnnotationNull:
@@ -437,8 +448,9 @@
 /*
  * Dumps the file header.
  */
-static void DumpFileHeader(const dex_ir::Header* header) {
+static void DumpFileHeader(dex_ir::Header* header) {
   char sanitized[8 * 2 + 1];
+  dex_ir::Collections& collections = header->GetCollections();
   fprintf(out_file_, "DEX file header:\n");
   Asciify(sanitized, header->Magic(), 8);
   fprintf(out_file_, "magic               : '%s'\n", sanitized);
@@ -452,24 +464,24 @@
   fprintf(out_file_, "link_size           : %d\n", header->LinkSize());
   fprintf(out_file_, "link_off            : %d (0x%06x)\n",
           header->LinkOffset(), header->LinkOffset());
-  fprintf(out_file_, "string_ids_size     : %d\n", header->StringIdsSize());
+  fprintf(out_file_, "string_ids_size     : %d\n", collections.StringIdsSize());
   fprintf(out_file_, "string_ids_off      : %d (0x%06x)\n",
-          header->StringIdsOffset(), header->StringIdsOffset());
-  fprintf(out_file_, "type_ids_size       : %d\n", header->TypeIdsSize());
+          collections.StringIdsOffset(), collections.StringIdsOffset());
+  fprintf(out_file_, "type_ids_size       : %d\n", collections.TypeIdsSize());
   fprintf(out_file_, "type_ids_off        : %d (0x%06x)\n",
-          header->TypeIdsOffset(), header->TypeIdsOffset());
-  fprintf(out_file_, "proto_ids_size      : %d\n", header->ProtoIdsSize());
+          collections.TypeIdsOffset(), collections.TypeIdsOffset());
+  fprintf(out_file_, "proto_ids_size      : %d\n", collections.ProtoIdsSize());
   fprintf(out_file_, "proto_ids_off       : %d (0x%06x)\n",
-          header->ProtoIdsOffset(), header->ProtoIdsOffset());
-  fprintf(out_file_, "field_ids_size      : %d\n", header->FieldIdsSize());
+          collections.ProtoIdsOffset(), collections.ProtoIdsOffset());
+  fprintf(out_file_, "field_ids_size      : %d\n", collections.FieldIdsSize());
   fprintf(out_file_, "field_ids_off       : %d (0x%06x)\n",
-          header->FieldIdsOffset(), header->FieldIdsOffset());
-  fprintf(out_file_, "method_ids_size     : %d\n", header->MethodIdsSize());
+          collections.FieldIdsOffset(), collections.FieldIdsOffset());
+  fprintf(out_file_, "method_ids_size     : %d\n", collections.MethodIdsSize());
   fprintf(out_file_, "method_ids_off      : %d (0x%06x)\n",
-          header->MethodIdsOffset(), header->MethodIdsOffset());
-  fprintf(out_file_, "class_defs_size     : %d\n", header->ClassDefsSize());
+          collections.MethodIdsOffset(), collections.MethodIdsOffset());
+  fprintf(out_file_, "class_defs_size     : %d\n", collections.ClassDefsSize());
   fprintf(out_file_, "class_defs_off      : %d (0x%06x)\n",
-          header->ClassDefsOffset(), header->ClassDefsOffset());
+          collections.ClassDefsOffset(), collections.ClassDefsOffset());
   fprintf(out_file_, "data_size           : %d\n", header->DataSize());
   fprintf(out_file_, "data_off            : %d (0x%06x)\n\n",
           header->DataOffset(), header->DataOffset());
@@ -480,19 +492,19 @@
  */
 static void DumpClassDef(dex_ir::Header* header, int idx) {
   // General class information.
-  dex_ir::ClassDef* class_def = header->ClassDefs()[idx].get();
+  dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
   fprintf(out_file_, "Class #%d header:\n", idx);
-  fprintf(out_file_, "class_idx           : %d\n", class_def->ClassType()->GetOffset());
+  fprintf(out_file_, "class_idx           : %d\n", class_def->ClassType()->GetIndex());
   fprintf(out_file_, "access_flags        : %d (0x%04x)\n",
           class_def->GetAccessFlags(), class_def->GetAccessFlags());
   uint32_t superclass_idx =  class_def->Superclass() == nullptr ?
-      DexFile::kDexNoIndex16 : class_def->Superclass()->GetOffset();
+      DexFile::kDexNoIndex16 : class_def->Superclass()->GetIndex();
   fprintf(out_file_, "superclass_idx      : %d\n", superclass_idx);
   fprintf(out_file_, "interfaces_off      : %d (0x%06x)\n",
           class_def->InterfacesOffset(), class_def->InterfacesOffset());
   uint32_t source_file_offset = 0xffffffffU;
   if (class_def->SourceFile() != nullptr) {
-    source_file_offset = class_def->SourceFile()->GetOffset();
+    source_file_offset = class_def->SourceFile()->GetIndex();
   }
   fprintf(out_file_, "source_file_idx     : %d\n", source_file_offset);
   uint32_t annotations_offset = 0;
@@ -541,7 +553,7 @@
     fputs("  empty-annotation-set\n", out_file_);
     return;
   }
-  for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : *set_item->GetItems()) {
+  for (dex_ir::AnnotationItem* annotation : *set_item->GetItems()) {
     if (annotation == nullptr) {
       continue;
     }
@@ -552,10 +564,7 @@
       case DexFile::kDexVisibilitySystem:  fputs("VISIBILITY_SYSTEM ",  out_file_); break;
       default:                             fputs("VISIBILITY_UNKNOWN ", out_file_); break;
     }  // switch
-    // Decode raw bytes in annotation.
-    // const uint8_t* rData = annotation->annotation_;
-    dex_ir::ArrayItem* data = annotation->GetItem();
-    DumpEncodedValue(data);
+    DumpEncodedAnnotation(annotation->GetAnnotation());
     fputc('\n', out_file_);
   }
 }
@@ -564,7 +573,7 @@
  * Dumps class annotations.
  */
 static void DumpClassAnnotations(dex_ir::Header* header, int idx) {
-  dex_ir::ClassDef* class_def = header->ClassDefs()[idx].get();
+  dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
   dex_ir::AnnotationsDirectoryItem* annotations_directory = class_def->Annotations();
   if (annotations_directory == nullptr) {
     return;  // none
@@ -587,7 +596,7 @@
   if (fields != nullptr) {
     for (auto& field : *fields) {
       const dex_ir::FieldId* field_id = field->GetFieldId();
-      const uint32_t field_idx = field_id->GetOffset();
+      const uint32_t field_idx = field_id->GetIndex();
       const char* field_name = field_id->Name()->Data();
       fprintf(out_file_, "Annotations on field #%u '%s'\n", field_idx, field_name);
       DumpAnnotationSetItem(field->GetAnnotationSetItem());
@@ -598,7 +607,7 @@
   if (methods != nullptr) {
     for (auto& method : *methods) {
       const dex_ir::MethodId* method_id = method->GetMethodId();
-      const uint32_t method_idx = method_id->GetOffset();
+      const uint32_t method_idx = method_id->GetIndex();
       const char* method_name = method_id->Name()->Data();
       fprintf(out_file_, "Annotations on method #%u '%s'\n", method_idx, method_name);
       DumpAnnotationSetItem(method->GetAnnotationSetItem());
@@ -609,13 +618,13 @@
   if (parameters != nullptr) {
     for (auto& parameter : *parameters) {
       const dex_ir::MethodId* method_id = parameter->GetMethodId();
-      const uint32_t method_idx = method_id->GetOffset();
+      const uint32_t method_idx = method_id->GetIndex();
       const char* method_name = method_id->Name()->Data();
       fprintf(out_file_, "Annotations on method #%u '%s' parameters\n", method_idx, method_name);
       uint32_t j = 0;
-      for (auto& annotation : *parameter->GetAnnotations()) {
+      for (dex_ir::AnnotationSetItem* annotation : *parameter->GetAnnotations()->GetItems()) {
         fprintf(out_file_, "#%u\n", j);
-        DumpAnnotationSetItem(annotation.get());
+        DumpAnnotationSetItem(annotation);
         ++j;
       }
     }
@@ -748,24 +757,24 @@
       outSize = snprintf(buf.get(), buf_size, "<no-index>");
       break;
     case Instruction::kIndexTypeRef:
-      if (index < header->TypeIdsSize()) {
-        const char* tp = header->TypeIds()[index]->GetStringId()->Data();
+      if (index < header->GetCollections().TypeIdsSize()) {
+        const char* tp = header->GetCollections().GetTypeId(index)->GetStringId()->Data();
         outSize = snprintf(buf.get(), buf_size, "%s // type@%0*x", tp, width, index);
       } else {
         outSize = snprintf(buf.get(), buf_size, "<type?> // type@%0*x", width, index);
       }
       break;
     case Instruction::kIndexStringRef:
-      if (index < header->StringIdsSize()) {
-        const char* st = header->StringIds()[index]->Data();
+      if (index < header->GetCollections().StringIdsSize()) {
+        const char* st = header->GetCollections().GetStringId(index)->Data();
         outSize = snprintf(buf.get(), buf_size, "\"%s\" // string@%0*x", st, width, index);
       } else {
         outSize = snprintf(buf.get(), buf_size, "<string?> // string@%0*x", width, index);
       }
       break;
     case Instruction::kIndexMethodRef:
-      if (index < header->MethodIdsSize()) {
-        dex_ir::MethodId* method_id = header->MethodIds()[index].get();
+      if (index < header->GetCollections().MethodIdsSize()) {
+        dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(index);
         const char* name = method_id->Name()->Data();
         std::string type_descriptor = GetSignatureForProtoId(method_id->Proto());
         const char* back_descriptor = method_id->Class()->GetStringId()->Data();
@@ -776,8 +785,8 @@
       }
       break;
     case Instruction::kIndexFieldRef:
-      if (index < header->FieldIdsSize()) {
-        dex_ir::FieldId* field_id = header->FieldIds()[index].get();
+      if (index < header->GetCollections().FieldIdsSize()) {
+        dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(index);
         const char* name = field_id->Name()->Data();
         const char* type_descriptor = field_id->Type()->GetStringId()->Data();
         const char* back_descriptor = field_id->Class()->GetStringId()->Data();
@@ -1028,7 +1037,7 @@
  */
 static void DumpBytecodes(dex_ir::Header* header, uint32_t idx,
                           const dex_ir::CodeItem* code, uint32_t code_offset) {
-  dex_ir::MethodId* method_id = header->MethodIds()[idx].get();
+  dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(idx);
   const char* name = method_id->Name()->Data();
   std::string type_descriptor = GetSignatureForProtoId(method_id->Proto());
   const char* back_descriptor = method_id->Class()->GetStringId()->Data();
@@ -1088,7 +1097,7 @@
     return;
   }
 
-  dex_ir::MethodId* method_id = header->MethodIds()[idx].get();
+  dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(idx);
   const char* name = method_id->Name()->Data();
   char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str());
   const char* back_descriptor = method_id->Class()->GetStringId()->Data();
@@ -1187,13 +1196,13 @@
  * Dumps a static (class) field.
  */
 static void DumpSField(dex_ir::Header* header, uint32_t idx, uint32_t flags,
-                       int i, dex_ir::ArrayItem* init) {
+                       int i, dex_ir::EncodedValue* init) {
   // Bail for anything private if export only requested.
   if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) {
     return;
   }
 
-  dex_ir::FieldId* field_id = header->FieldIds()[idx].get();
+  dex_ir::FieldId* field_id = header->GetCollections().GetFieldId(idx);
   const char* name = field_id->Name()->Data();
   const char* type_descriptor = field_id->Type()->GetStringId()->Data();
   const char* back_descriptor = field_id->Class()->GetStringId()->Data();
@@ -1293,7 +1302,7 @@
                       dex_ir::Header* header,
                       int idx,
                       char** last_package) {
-  dex_ir::ClassDef* class_def = header->ClassDefs()[idx].get();
+  dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
   // Omitting non-public class.
   if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) {
     return;
@@ -1316,7 +1325,8 @@
   // 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.
-  const char* class_descriptor = header->ClassDefs()[idx]->ClassType()->GetStringId()->Data();
+  const char* class_descriptor =
+      header->GetCollections().GetClassDef(idx)->ClassType()->GetStringId()->Data();
   if (!(class_descriptor[0] == 'L' &&
         class_descriptor[strlen(class_descriptor)-1] == ';')) {
     // Arrays and primitives should not be defined explicitly. Keep going?
@@ -1386,7 +1396,7 @@
   }
 
   // Interfaces.
-  dex_ir::TypeIdVector* interfaces = class_def->Interfaces();
+  const dex_ir::TypeIdVector* interfaces = class_def->Interfaces();
   if (interfaces != nullptr) {
     for (uint32_t i = 0; i < interfaces->size(); i++) {
       DumpInterface((*interfaces)[i], i);
@@ -1396,8 +1406,10 @@
   // Fields and methods.
   dex_ir::ClassData* class_data = class_def->GetClassData();
   // Prepare data for static fields.
-  std::vector<std::unique_ptr<dex_ir::ArrayItem>>* static_values = class_def->StaticValues();
-  const uint32_t static_values_size = (static_values == nullptr) ? 0 : static_values->size();
+  dex_ir::EncodedArrayItem* static_values = class_def->StaticValues();
+  dex_ir::EncodedValueVector* encoded_values =
+      static_values == nullptr ? nullptr : static_values->GetEncodedValues();
+  const uint32_t encoded_values_size = (encoded_values == nullptr) ? 0 : encoded_values->size();
 
   // Static fields.
   if (options_.output_format_ == kOutputPlain) {
@@ -1408,10 +1420,10 @@
     if (static_fields != nullptr) {
       for (uint32_t i = 0; i < static_fields->size(); i++) {
         DumpSField(header,
-                   (*static_fields)[i]->GetFieldId()->GetOffset(),
+                   (*static_fields)[i]->GetFieldId()->GetIndex(),
                    (*static_fields)[i]->GetAccessFlags(),
                    i,
-                   i < static_values_size ? (*static_values)[i].get() : nullptr);
+                   i < encoded_values_size ? (*encoded_values)[i].get() : nullptr);
       }  // for
     }
   }
@@ -1425,7 +1437,7 @@
     if (instance_fields != nullptr) {
       for (uint32_t i = 0; i < instance_fields->size(); i++) {
         DumpIField(header,
-                   (*instance_fields)[i]->GetFieldId()->GetOffset(),
+                   (*instance_fields)[i]->GetFieldId()->GetIndex(),
                    (*instance_fields)[i]->GetAccessFlags(),
                    i);
       }  // for
@@ -1441,7 +1453,7 @@
     if (direct_methods != nullptr) {
       for (uint32_t i = 0; i < direct_methods->size(); i++) {
         DumpMethod(header,
-                   (*direct_methods)[i]->GetMethodId()->GetOffset(),
+                   (*direct_methods)[i]->GetMethodId()->GetIndex(),
                    (*direct_methods)[i]->GetAccessFlags(),
                    (*direct_methods)[i]->GetCodeItem(),
                  i);
@@ -1458,7 +1470,7 @@
     if (virtual_methods != nullptr) {
       for (uint32_t i = 0; i < virtual_methods->size(); i++) {
         DumpMethod(header,
-                   (*virtual_methods)[i]->GetMethodId()->GetOffset(),
+                   (*virtual_methods)[i]->GetMethodId()->GetIndex(),
                    (*virtual_methods)[i]->GetAccessFlags(),
                    (*virtual_methods)[i]->GetCodeItem(),
                    i);
@@ -1474,7 +1486,7 @@
     }
     const dex_ir::StringId* source_file = class_def->SourceFile();
     fprintf(out_file_, "  source_file_idx   : %d (%s)\n\n",
-            source_file == nullptr ? 0xffffffffU : source_file->GetOffset(), file_name);
+            source_file == nullptr ? 0xffffffffU : source_file->GetIndex(), file_name);
   } else if (options_.output_format_ == kOutputXml) {
     fprintf(out_file_, "</class>\n");
   }
@@ -1483,6 +1495,96 @@
 }
 
 /*
+static uint32_t GetDataSectionOffset(dex_ir::Header& header) {
+  return dex_ir::Header::ItemSize() +
+      header.GetCollections().StringIdsSize() * dex_ir::StringId::ItemSize() +
+      header.GetCollections().TypeIdsSize() * dex_ir::TypeId::ItemSize() +
+      header.GetCollections().ProtoIdsSize() * dex_ir::ProtoId::ItemSize() +
+      header.GetCollections().FieldIdsSize() * dex_ir::FieldId::ItemSize() +
+      header.GetCollections().MethodIdsSize() * dex_ir::MethodId::ItemSize() +
+      header.GetCollections().ClassDefsSize() * dex_ir::ClassDef::ItemSize();
+}
+
+static bool Align(File* file, uint32_t& offset) {
+  uint8_t zero_buffer[] = { 0, 0, 0 };
+  uint32_t zeroes = (-offset) & 3;
+  if (zeroes > 0) {
+    if (!file->PwriteFully(zero_buffer, zeroes, offset)) {
+      return false;
+    }
+    offset += zeroes;
+  }
+  return true;
+}
+
+static bool WriteStrings(File* dex_file, dex_ir::Header& header,
+                         uint32_t& index_offset, uint32_t& data_offset) {
+  uint32_t index = 0;
+  uint32_t index_buffer[1];
+  uint32_t string_length;
+  uint32_t length_length;
+  uint8_t length_buffer[8];
+  for (std::unique_ptr<dex_ir::StringId>& string_id : header.GetCollections().StringIds()) {
+    string_id->SetOffset(index);
+    index_buffer[0] = data_offset;
+    string_length = strlen(string_id->Data());
+    length_length = UnsignedLeb128Size(string_length);
+    EncodeUnsignedLeb128(length_buffer, string_length);
+
+    if (!dex_file->PwriteFully(index_buffer, 4, index_offset) ||
+        !dex_file->PwriteFully(length_buffer, length_length, data_offset) ||
+        !dex_file->PwriteFully(string_id->Data(), string_length, data_offset + length_length)) {
+      return false;
+    }
+
+    index++;
+    index_offset += 4;
+    data_offset += string_length + length_length;
+  }
+  return true;
+}
+
+static bool WriteTypes(File* dex_file, dex_ir::Header& header, uint32_t& index_offset) {
+  uint32_t index = 0;
+  uint32_t index_buffer[1];
+  for (std::unique_ptr<dex_ir::TypeId>& type_id : header.GetCollections().TypeIds()) {
+    type_id->SetIndex(index);
+    index_buffer[0] = type_id->GetStringId()->GetOffset();
+
+    if (!dex_file->PwriteFully(index_buffer, 4, index_offset)) {
+      return false;
+    }
+
+    index++;
+    index_offset += 4;
+  }
+  return true;
+}
+
+static bool WriteTypeLists(File* dex_file, dex_ir::Header& header, uint32_t& data_offset) {
+  if (!Align(dex_file, data_offset)) {
+    return false;
+  }
+
+  return true;
+}
+
+static void OutputDexFile(dex_ir::Header& header, const char* file_name) {
+  LOG(INFO) << "FILE NAME: " << file_name;
+  std::unique_ptr<File> dex_file(OS::CreateEmptyFileWriteOnly(file_name));
+  if (dex_file == nullptr) {
+    fprintf(stderr, "Can't open %s\n", file_name);
+    return;
+  }
+
+  uint32_t index_offset = dex_ir::Header::ItemSize();
+  uint32_t data_offset = GetDataSectionOffset(header);
+  WriteStrings(dex_file.get(), header, index_offset, data_offset);
+  WriteTypes(dex_file.get(), header, index_offset);
+}
+*/
+
+/*
  * Dumps the requested sections of the file.
  */
 static void ProcessDexFile(const char* file_name, const DexFile* dex_file) {
@@ -1504,7 +1606,7 @@
 
   // Iterate over all classes.
   char* package = nullptr;
-  const uint32_t class_defs_size = header->ClassDefsSize();
+  const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
   for (uint32_t i = 0; i < class_defs_size; i++) {
     DumpClass(dex_file, header.get(), i, &package);
   }  // for
@@ -1519,6 +1621,14 @@
   if (options_.output_format_ == kOutputXml) {
     fprintf(out_file_, "</api>\n");
   }
+
+  /*
+  // Output dex file.
+  if (options_.output_dex_files_) {
+    std::string output_dex_filename = dex_file->GetLocation() + ".out";
+    OutputDexFile(*header, output_dex_filename.c_str());
+  }
+  */
 }
 
 /*
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index bae587d..736d230 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -41,6 +41,7 @@
   bool disassemble_;
   bool exports_only_;
   bool ignore_bad_checksum_;
+  bool output_dex_files_;
   bool show_annotations_;
   bool show_cfg_;
   bool show_file_headers_;
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 09fa0ef..ec5edf4 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -38,7 +38,7 @@
  */
 static void Usage(void) {
   fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
-  fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
+  fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-w]"
                   " dexfile...\n\n", kProgramName);
   fprintf(stderr, " -a : display annotations\n");
   fprintf(stderr, " -b : build dex_ir\n");
@@ -51,6 +51,7 @@
   fprintf(stderr, " -i : ignore checksum failures\n");
   fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
   fprintf(stderr, " -o : output file name (defaults to stdout)\n");
+  fprintf(stderr, " -w : output dex files\n");
 }
 
 /*
@@ -68,7 +69,7 @@
 
   // Parse all arguments.
   while (1) {
-    const int ic = getopt(argc, argv, "abcdefghil:o:");
+    const int ic = getopt(argc, argv, "abcdefghil:o:w");
     if (ic < 0) {
       break;  // done
     }
@@ -113,6 +114,9 @@
       case 'o':  // output file
         options_.output_file_name_ = optarg;
         break;
+      case 'w':  // output dex files
+        options_.output_dex_files_ = true;
+        break;
       default:
         want_usage = true;
         break;
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 925047f..30b708c 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -116,8 +116,7 @@
 
  public:
   CustomDisassembler(std::ostream& os, const DisassemblerOptions* options)
-      // vixl::aarch32::Disassembler::~Disassembler() will delete the stream.
-      : PrintDisassembler(new CustomDisassemblerStream(os, this, options)) {}
+      : PrintDisassembler(&disassembler_stream_), disassembler_stream_(os, this, options) {}
 
   void PrintPc(uint32_t prog_ctr) OVERRIDE {
     os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": ";
@@ -133,6 +132,7 @@
 
  private:
   bool is_t32_;
+  CustomDisassemblerStream disassembler_stream_;
 };
 
 void DisassemblerArm::CustomDisassembler::CustomDisassemblerStream::PrintLiteral(LocationType type,
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 7837d66..eaeb78e 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -26,7 +26,10 @@
     // that the image it's analyzing be the same ISA as the runtime ISA.
     compile_multilib: "both",
 
-    shared_libs: ["libbacktrace"],
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+    ],
     target: {
         android: {
             shared_libs: ["libcutils"],
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 106cf2f..ace21aa 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -34,7 +34,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "image.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "os.h"
 
 #include "cmdline.h"
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index dd6331c..bbe6cc1 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -34,6 +34,7 @@
         "libart",
         "libart-compiler",
         "libart-disassembler",
+        "libbase",
     ],
 }
 
@@ -47,6 +48,7 @@
         "libartd",
         "libartd-compiler",
         "libartd-disassembler",
+        "libbase",
     ],
 }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index d8ac581..be5224b 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -23,6 +23,7 @@
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "arch/instruction_set_features.h"
@@ -42,7 +43,9 @@
 #include "gc/space/large_object_space.h"
 #include "gc/space/space-inl.h"
 #include "image-inl.h"
+#include "imtable-inl.h"
 #include "indenter.h"
+#include "interpreter/unstarted_runtime.h"
 #include "linker/buffered_output_stream.h"
 #include "linker/file_output_stream.h"
 #include "mirror/array-inl.h"
@@ -55,7 +58,7 @@
 #include "oat_file_manager.h"
 #include "os.h"
 #include "safe_map.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "stack_map.h"
 #include "string_reference.h"
@@ -2443,39 +2446,55 @@
   return EXIT_SUCCESS;
 }
 
-static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options,
-                              std::ostream* os) {
-  CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
-
+static jobject InstallOatFile(Runtime* runtime,
+                              std::unique_ptr<OatFile> oat_file,
+                              std::vector<const DexFile*>* class_path)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   Thread* self = Thread::Current();
   CHECK(self != nullptr);
   // Need well-known-classes.
   WellKnownClasses::Init(self->GetJniEnv());
 
   // Need to register dex files to get a working dex cache.
-  ScopedObjectAccess soa(self);
+  OatFile* oat_file_ptr = oat_file.get();
   ClassLinker* class_linker = runtime->GetClassLinker();
-  runtime->GetOatFileManager().RegisterOatFile(std::unique_ptr<const OatFile>(oat_file));
-  std::vector<const DexFile*> class_path;
-  for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
+  runtime->GetOatFileManager().RegisterOatFile(std::move(oat_file));
+  for (const OatFile::OatDexFile* odf : oat_file_ptr->GetOatDexFiles()) {
     std::string error_msg;
     const DexFile* const dex_file = OpenDexFile(odf, &error_msg);
     CHECK(dex_file != nullptr) << error_msg;
     class_linker->RegisterDexFile(*dex_file, nullptr);
-    class_path.push_back(dex_file);
+    class_path->push_back(dex_file);
   }
 
-  // Need a class loader.
-  // Fake that we're a compiler.
-  jobject class_loader = class_linker->CreatePathClassLoader(self, class_path);
+  // Need a class loader. Fake that we're a compiler.
+  // Note: this will run initializers through the unstarted runtime, so make sure it's
+  //       initialized.
+  interpreter::UnstartedRuntime::Initialize();
+
+  jobject class_loader = class_linker->CreatePathClassLoader(self, *class_path);
+
+  return class_loader;
+}
+
+static int DumpOatWithRuntime(Runtime* runtime,
+                              std::unique_ptr<OatFile> oat_file,
+                              OatDumperOptions* options,
+                              std::ostream* os) {
+  CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
+  ScopedObjectAccess soa(Thread::Current());
+
+  OatFile* oat_file_ptr = oat_file.get();
+  std::vector<const DexFile*> class_path;
+  jobject class_loader = InstallOatFile(runtime, std::move(oat_file), &class_path);
 
   // Use the class loader while dumping.
-  StackHandleScope<1> scope(self);
+  StackHandleScope<1> scope(soa.Self());
   Handle<mirror::ClassLoader> loader_handle = scope.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(class_loader));
+      soa.Decode<mirror::ClassLoader>(class_loader));
   options->class_loader_ = &loader_handle;
 
-  OatDumper oat_dumper(*oat_file, *options);
+  OatDumper oat_dumper(*oat_file_ptr, *options);
   bool success = oat_dumper.Dump(*os);
   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
@@ -2494,23 +2513,23 @@
 static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
                    std::ostream* os) {
   std::string error_msg;
-  OatFile* oat_file = OatFile::Open(oat_filename,
-                                    oat_filename,
-                                    nullptr,
-                                    nullptr,
-                                    false,
-                                    /*low_4gb*/false,
-                                    nullptr,
-                                    &error_msg);
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+                                                  oat_filename,
+                                                  nullptr,
+                                                  nullptr,
+                                                  false,
+                                                  /*low_4gb*/false,
+                                                  nullptr,
+                                                  &error_msg));
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
   }
 
   if (runtime != nullptr) {
-    return DumpOatWithRuntime(runtime, oat_file, options, os);
+    return DumpOatWithRuntime(runtime, std::move(oat_file), options, os);
   } else {
-    return DumpOatWithoutRuntime(oat_file, options, os);
+    return DumpOatWithoutRuntime(oat_file.get(), options, os);
   }
 }
 
@@ -2547,6 +2566,444 @@
   return EXIT_SUCCESS;
 }
 
+class IMTDumper {
+ public:
+  static bool Dump(Runtime* runtime,
+                   const std::string& imt_file,
+                   bool dump_imt_stats,
+                   const char* oat_filename) {
+    Thread* self = Thread::Current();
+
+    ScopedObjectAccess soa(self);
+    StackHandleScope<1> scope(self);
+    MutableHandle<mirror::ClassLoader> class_loader = scope.NewHandle<mirror::ClassLoader>(nullptr);
+    std::vector<const DexFile*> class_path;
+
+    if (oat_filename != nullptr) {
+      std::string error_msg;
+      std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_filename,
+                                                      oat_filename,
+                                                      nullptr,
+                                                      nullptr,
+                                                      false,
+                                                      /*low_4gb*/false,
+                                                      nullptr,
+                                                      &error_msg));
+      if (oat_file == nullptr) {
+        fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+        return false;
+      }
+
+      class_loader.Assign(soa.Decode<mirror::ClassLoader>(
+          InstallOatFile(runtime, std::move(oat_file), &class_path)));
+    } else {
+      class_loader.Assign(nullptr);  // Boot classloader. Just here for explicit documentation.
+      class_path = runtime->GetClassLinker()->GetBootClassPath();
+    }
+
+    if (!imt_file.empty()) {
+      return DumpImt(runtime, imt_file, class_loader);
+    }
+
+    if (dump_imt_stats) {
+      return DumpImtStats(runtime, class_path, class_loader);
+    }
+
+    LOG(FATAL) << "Should not reach here";
+    UNREACHABLE();
+  }
+
+ private:
+  static bool DumpImt(Runtime* runtime,
+                      const std::string& imt_file,
+                      Handle<mirror::ClassLoader> h_class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    std::vector<std::string> lines = ReadCommentedInputFromFile(imt_file);
+    std::unordered_set<std::string> prepared;
+
+    for (const std::string& line : lines) {
+      // A line should be either a class descriptor, in which case we will dump the complete IMT,
+      // or a class descriptor and an interface method, in which case we will lookup the method,
+      // determine its IMT slot, and check the class' IMT.
+      size_t first_space = line.find(' ');
+      if (first_space == std::string::npos) {
+        DumpIMTForClass(runtime, line, h_class_loader, &prepared);
+      } else {
+        DumpIMTForMethod(runtime,
+                         line.substr(0, first_space),
+                         line.substr(first_space + 1, std::string::npos),
+                         h_class_loader,
+                         &prepared);
+      }
+      std::cerr << std::endl;
+    }
+
+    return true;
+  }
+
+  static bool DumpImtStats(Runtime* runtime,
+                           const std::vector<const DexFile*>& dex_files,
+                           Handle<mirror::ClassLoader> h_class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    size_t without_imt = 0;
+    size_t with_imt = 0;
+    std::map<size_t, size_t> histogram;
+
+    ClassLinker* class_linker = runtime->GetClassLinker();
+    const PointerSize pointer_size = class_linker->GetImagePointerSize();
+    std::unordered_set<std::string> prepared;
+
+    Thread* self = Thread::Current();
+    StackHandleScope<1> scope(self);
+    MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr));
+
+    for (const DexFile* dex_file : dex_files) {
+      for (uint32_t class_def_index = 0;
+           class_def_index != dex_file->NumClassDefs();
+           ++class_def_index) {
+        const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+        const char* descriptor = dex_file->GetClassDescriptor(class_def);
+        h_klass.Assign(class_linker->FindClass(self, descriptor, h_class_loader));
+        if (h_klass.Get() == nullptr) {
+          std::cerr << "Warning: could not load " << descriptor << std::endl;
+          continue;
+        }
+
+        if (HasNoIMT(runtime, h_klass, pointer_size, &prepared)) {
+          without_imt++;
+          continue;
+        }
+
+        ImTable* im_table = PrepareAndGetImTable(runtime, h_klass, pointer_size, &prepared);
+        if (im_table == nullptr) {
+          // Should not happen, but accept.
+          without_imt++;
+          continue;
+        }
+
+        with_imt++;
+        for (size_t imt_index = 0; imt_index != ImTable::kSize; ++imt_index) {
+          ArtMethod* ptr = im_table->Get(imt_index, pointer_size);
+          if (ptr->IsRuntimeMethod()) {
+            if (ptr->IsImtUnimplementedMethod()) {
+              histogram[0]++;
+            } else {
+              ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+              histogram[current_table->NumEntries(pointer_size)]++;
+            }
+          } else {
+            histogram[1]++;
+          }
+        }
+      }
+    }
+
+    std::cerr << "IMT stats:"
+              << std::endl << std::endl;
+
+    std::cerr << "  " << with_imt << " classes with IMT."
+              << std::endl << std::endl;
+    std::cerr << "  " << without_imt << " classes without IMT (or copy from Object)."
+              << std::endl << std::endl;
+
+    double sum_one = 0;
+    size_t count_one = 0;
+
+    std::cerr << "  " << "IMT histogram" << std::endl;
+    for (auto& bucket : histogram) {
+      std::cerr << "    " << bucket.first << " " << bucket.second << std::endl;
+      if (bucket.first > 0) {
+        sum_one += bucket.second * bucket.first;
+        count_one += bucket.second;
+      }
+    }
+
+    double count_zero = count_one + histogram[0];
+    std::cerr << "   Stats:" << std::endl;
+    std::cerr << "     Average depth (including empty): " << (sum_one / count_zero) << std::endl;
+    std::cerr << "     Average depth (excluding empty): " << (sum_one / count_one) << std::endl;
+
+    return true;
+  }
+
+  // Return whether the given class has no IMT (or the one shared with java.lang.Object).
+  static bool HasNoIMT(Runtime* runtime,
+                       Handle<mirror::Class> klass,
+                       const PointerSize pointer_size,
+                       std::unordered_set<std::string>* prepared)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (klass->IsObjectClass() || !klass->ShouldHaveImt()) {
+      return true;
+    }
+
+    if (klass->GetImt(pointer_size) == nullptr) {
+      PrepareClass(runtime, klass, prepared);
+    }
+
+    mirror::Class* object_class = mirror::Class::GetJavaLangClass()->GetSuperClass();
+    DCHECK(object_class->IsObjectClass());
+
+    bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size);
+
+    if (klass->GetIfTable() == nullptr) {
+      DCHECK(result);
+    }
+
+    return result;
+  }
+
+  static void PrintTable(ImtConflictTable* table, PointerSize pointer_size)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (table == nullptr) {
+      std::cerr << "    <No IMT?>" << std::endl;
+      return;
+    }
+    size_t table_index = 0;
+    for (;;) {
+      ArtMethod* ptr = table->GetInterfaceMethod(table_index, pointer_size);
+      if (ptr == nullptr) {
+        return;
+      }
+      table_index++;
+      std::cerr << "    " << PrettyMethod(ptr, true) << std::endl;
+    }
+  }
+
+  static ImTable* PrepareAndGetImTable(Runtime* runtime,
+                                       Thread* self,
+                                       Handle<mirror::ClassLoader> h_loader,
+                                       const std::string& class_name,
+                                       const PointerSize pointer_size,
+                                       mirror::Class** klass_out,
+                                       std::unordered_set<std::string>* prepared)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (class_name.empty()) {
+      return nullptr;
+    }
+
+    std::string descriptor;
+    if (class_name[0] == 'L') {
+      descriptor = class_name;
+    } else {
+      descriptor = DotToDescriptor(class_name.c_str());
+    }
+
+    mirror::Class* klass = runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), h_loader);
+
+    if (klass == nullptr) {
+      self->ClearException();
+      std::cerr << "Did not find " <<  class_name << std::endl;
+      *klass_out = nullptr;
+      return nullptr;
+    }
+
+    StackHandleScope<1> scope(Thread::Current());
+    Handle<mirror::Class> h_klass = scope.NewHandle<mirror::Class>(klass);
+
+    ImTable* ret = PrepareAndGetImTable(runtime, h_klass, pointer_size, prepared);
+    *klass_out = h_klass.Get();
+    return ret;
+  }
+
+  static ImTable* PrepareAndGetImTable(Runtime* runtime,
+                                       Handle<mirror::Class> h_klass,
+                                       const PointerSize pointer_size,
+                                       std::unordered_set<std::string>* prepared)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    PrepareClass(runtime, h_klass, prepared);
+    return h_klass->GetImt(pointer_size);
+  }
+
+  static void DumpIMTForClass(Runtime* runtime,
+                              const std::string& class_name,
+                              Handle<mirror::ClassLoader> h_loader,
+                              std::unordered_set<std::string>* prepared)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+    mirror::Class* klass;
+    ImTable* imt = PrepareAndGetImTable(runtime,
+                                        Thread::Current(),
+                                        h_loader,
+                                        class_name,
+                                        pointer_size,
+                                        &klass,
+                                        prepared);
+    if (imt == nullptr) {
+      return;
+    }
+
+    std::cerr << class_name << std::endl << " IMT:" << std::endl;
+    for (size_t index = 0; index < ImTable::kSize; ++index) {
+      std::cerr << "  " << index << ":" << std::endl;
+      ArtMethod* ptr = imt->Get(index, pointer_size);
+      if (ptr->IsRuntimeMethod()) {
+        if (ptr->IsImtUnimplementedMethod()) {
+          std::cerr << "    <empty>" << std::endl;
+        } else {
+          ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+          PrintTable(current_table, pointer_size);
+        }
+      } else {
+        std::cerr << "    " << PrettyMethod(ptr, true) << std::endl;
+      }
+    }
+
+    std::cerr << " Interfaces:" << std::endl;
+    // Run through iftable, find methods that slot here, see if they fit.
+    mirror::IfTable* if_table = klass->GetIfTable();
+    if (if_table != nullptr) {
+      for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
+        mirror::Class* iface = if_table->GetInterface(i);
+        std::string iface_name;
+        std::cerr << "  " << iface->GetDescriptor(&iface_name) << std::endl;
+
+        for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
+          uint32_t base_hash = ImTable::GetBaseImtHash(&iface_method);
+          uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
+          std::cerr << "    " << PrettyMethod(&iface_method, true) << " slot=" << std::dec
+              << imt_slot << " base_hash=0x" << std::hex << base_hash << std::endl;
+        }
+      }
+    }
+  }
+
+  static void DumpIMTForMethod(Runtime* runtime,
+                               const std::string& class_name,
+                               const std::string& method,
+                               Handle<mirror::ClassLoader> h_loader,
+                               std::unordered_set<std::string>* prepared)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+    mirror::Class* klass;
+    ImTable* imt = PrepareAndGetImTable(runtime,
+                                        Thread::Current(),
+                                        h_loader,
+                                        class_name,
+                                        pointer_size,
+                                        &klass,
+                                        prepared);
+    if (imt == nullptr) {
+      return;
+    }
+
+    std::cerr << class_name << " <" << method << ">" << std::endl;
+    for (size_t index = 0; index < ImTable::kSize; ++index) {
+      ArtMethod* ptr = imt->Get(index, pointer_size);
+      if (ptr->IsRuntimeMethod()) {
+        if (ptr->IsImtUnimplementedMethod()) {
+          continue;
+        }
+
+        ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+        if (current_table == nullptr) {
+          continue;
+        }
+
+        size_t table_index = 0;
+        for (;;) {
+          ArtMethod* ptr2 = current_table->GetInterfaceMethod(table_index, pointer_size);
+          if (ptr2 == nullptr) {
+            break;
+          }
+          table_index++;
+
+          std::string p_name = PrettyMethod(ptr2, true);
+          if (StartsWith(p_name, method.c_str())) {
+            std::cerr << "  Slot "
+                      << index
+                      << " ("
+                      << current_table->NumEntries(pointer_size)
+                      << ")"
+                      << std::endl;
+            PrintTable(current_table, pointer_size);
+            return;
+          }
+        }
+      } else {
+        std::string p_name = PrettyMethod(ptr, true);
+        if (StartsWith(p_name, method.c_str())) {
+          std::cerr << "  Slot " << index << " (1)" << std::endl;
+          std::cerr << "    " << p_name << std::endl;
+        } else {
+          // Run through iftable, find methods that slot here, see if they fit.
+          mirror::IfTable* if_table = klass->GetIfTable();
+          if (if_table != nullptr) {
+            for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
+              mirror::Class* iface = if_table->GetInterface(i);
+              size_t num_methods = iface->NumDeclaredVirtualMethods();
+              if (num_methods > 0) {
+                for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) {
+                  if (ImTable::GetImtIndex(&iface_method) == index) {
+                    std::string i_name = PrettyMethod(&iface_method, true);
+                    if (StartsWith(i_name, method.c_str())) {
+                      std::cerr << "  Slot " << index << " (1)" << std::endl;
+                      std::cerr << "    " << p_name << " (" << i_name << ")" << std::endl;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // Read lines from the given stream, dropping comments and empty lines
+  static std::vector<std::string> ReadCommentedInputStream(std::istream& in_stream) {
+    std::vector<std::string> output;
+    while (in_stream.good()) {
+      std::string dot;
+      std::getline(in_stream, dot);
+      if (StartsWith(dot, "#") || dot.empty()) {
+        continue;
+      }
+      output.push_back(dot);
+    }
+    return output;
+  }
+
+  // Read lines from the given file, dropping comments and empty lines.
+  static std::vector<std::string> ReadCommentedInputFromFile(const std::string& input_filename) {
+    std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
+    if (input_file.get() == nullptr) {
+      LOG(ERROR) << "Failed to open input file " << input_filename;
+      return std::vector<std::string>();
+    }
+    std::vector<std::string> result = ReadCommentedInputStream(*input_file);
+    input_file->close();
+    return result;
+  }
+
+  // Prepare a class, i.e., ensure it has a filled IMT. Will do so recursively for superclasses,
+  // and note in the given set that the work was done.
+  static void PrepareClass(Runtime* runtime,
+                           Handle<mirror::Class> h_klass,
+                           std::unordered_set<std::string>* done)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!h_klass->ShouldHaveImt()) {
+      return;
+    }
+
+    std::string name;
+    name = h_klass->GetDescriptor(&name);
+
+    if (done->find(name) != done->end()) {
+      return;
+    }
+    done->insert(name);
+
+    if (h_klass->HasSuperClass()) {
+      StackHandleScope<1> h(Thread::Current());
+      PrepareClass(runtime, h.NewHandle<mirror::Class>(h_klass->GetSuperClass()), done);
+    }
+
+    if (!h_klass->IsTemp()) {
+      runtime->GetClassLinker()->FillIMTAndConflictTables(h_klass.Get());
+    }
+  }
+};
+
 struct OatdumpArgs : public CmdlineArgs {
  protected:
   using Base = CmdlineArgs;
@@ -2596,6 +3053,10 @@
       app_image_ = option.substr(strlen("--app-image=")).data();
     } else if (option.starts_with("--app-oat=")) {
       app_oat_ = option.substr(strlen("--app-oat=")).data();
+    } else if (option.starts_with("--dump-imt=")) {
+      imt_dump_ = option.substr(strlen("--dump-imt=")).data();
+    } else if (option == "--dump-imt-stats") {
+      imt_stat_dump_ = true;
     } else {
       return kParseUnknownArgument;
     }
@@ -2692,6 +3153,16 @@
         "  --addr2instr=<address>: output matching method disassembled code from relative\n"
         "                          address (e.g. PC from crash dump)\n"
         "      Example: --addr2instr=0x00001a3b\n"
+        "\n"
+        "  --dump-imt=<file.txt>: output IMT collisions (if any) for the given receiver\n"
+        "                         types and interface methods in the given file. The file\n"
+        "                         is read line-wise, where each line should either be a class\n"
+        "                         name or descriptor, or a class name/descriptor and a prefix\n"
+        "                         of a complete method name (separated by a whitespace).\n"
+        "      Example: --dump-imt=imt.txt\n"
+        "\n"
+        "  --dump-imt-stats: output IMT statistics for the given boot image\n"
+        "      Example: --dump-imt-stats"
         "\n";
 
     return usage;
@@ -2703,6 +3174,7 @@
   const char* method_filter_ = "";
   const char* image_location_ = nullptr;
   std::string elf_filename_prefix_;
+  std::string imt_dump_;
   bool dump_vmap_ = true;
   bool dump_code_info_stack_maps_ = false;
   bool disassemble_code_ = true;
@@ -2711,6 +3183,7 @@
   bool list_classes_ = false;
   bool list_methods_ = false;
   bool dump_header_only_ = false;
+  bool imt_stat_dump_ = false;
   uint32_t addr2instr_ = 0;
   const char* export_dex_location_ = nullptr;
   const char* app_image_ = nullptr;
@@ -2739,7 +3212,9 @@
         args_->app_oat_,
         args_->addr2instr_));
 
-    return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
+    return (args_->boot_image_location_ != nullptr ||
+            args_->image_location_ != nullptr ||
+            !args_->imt_dump_.empty()) &&
           !args_->symbolize_;
   }
 
@@ -2767,6 +3242,13 @@
   virtual bool ExecuteWithRuntime(Runtime* runtime) {
     CHECK(args_ != nullptr);
 
+    if (!args_->imt_dump_.empty() || args_->imt_stat_dump_) {
+      return IMTDumper::Dump(runtime,
+                             args_->imt_dump_,
+                             args_->imt_stat_dump_,
+                             args_->oat_filename_);
+    }
+
     if (args_->oat_filename_ != nullptr) {
       return DumpOat(runtime,
                      args_->oat_filename_,
diff --git a/patchoat/Android.bp b/patchoat/Android.bp
index 8d8d6d1..a78f97d 100644
--- a/patchoat/Android.bp
+++ b/patchoat/Android.bp
@@ -24,6 +24,9 @@
             compile_multilib: "prefer32",
         },
     },
+    shared_libs: [
+        "libbase",
+    ],
 }
 
 art_cc_binary {
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 5240011..d58f38c 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -37,8 +37,8 @@
 #include "elf_file_impl.h"
 #include "gc/space/image_space.h"
 #include "image-inl.h"
-#include "mirror/abstract_method.h"
 #include "mirror/dex_cache.h"
+#include "mirror/executable.h"
 #include "mirror/object-inl.h"
 #include "mirror/method.h"
 #include "mirror/reference.h"
@@ -46,7 +46,7 @@
 #include "offsets.h"
 #include "os.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "utils.h"
 
@@ -770,8 +770,8 @@
   } else if (object->GetClass() == mirror::Method::StaticClass() ||
              object->GetClass() == mirror::Constructor::StaticClass()) {
     // Need to go update the ArtMethod.
-    auto* dest = down_cast<mirror::AbstractMethod*>(copy);
-    auto* src = down_cast<mirror::AbstractMethod*>(object);
+    auto* dest = down_cast<mirror::Executable*>(copy);
+    auto* src = down_cast<mirror::Executable*>(object);
     dest->SetArtMethod(RelocatedAddressOfPointer(src->GetArtMethod()));
   }
 }
diff --git a/profman/Android.bp b/profman/Android.bp
index 322dda2..2dcbaee 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -32,6 +32,10 @@
     include_dirs: [
         "art/cmdline",
     ],
+
+    shared_libs: [
+        "libbase",
+    ],
 }
 
 art_cc_binary {
diff --git a/profman/profman.cc b/profman/profman.cc
index a5fefa7..7722e80 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -280,18 +280,11 @@
     for (size_t i = 0; i < dex_locations_.size(); ++i) {
       std::string error_msg;
       std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
-      std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(apks_fd_[i],
-                                                                     dex_locations_[i].c_str(),
-                                                                     &error_msg));
-      if (zip_archive == nullptr) {
-        LOG(WARNING) << "OpenFromFd failed for '" << dex_locations_[i] << "' " << error_msg;
-        continue;
-      }
-      if (DexFile::OpenFromZip(*zip_archive,
-                               dex_locations_[i],
-                               kVerifyChecksum,
-                               &error_msg,
-                               &dex_files_for_location)) {
+      if (DexFile::OpenZip(apks_fd_[i],
+                           dex_locations_[i],
+                           kVerifyChecksum,
+                           &error_msg,
+                           &dex_files_for_location)) {
       } else {
         LOG(WARNING) << "OpenFromZip failed for '" << dex_locations_[i] << "' " << error_msg;
         continue;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 8c17653..31f2490 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -120,12 +120,14 @@
         "linear_alloc.cc",
         "mem_map.cc",
         "memory_region.cc",
-        "mirror/abstract_method.cc",
         "mirror/array.cc",
         "mirror/class.cc",
         "mirror/dex_cache.cc",
+        "mirror/executable.cc",
         "mirror/field.cc",
         "mirror/method.cc",
+        "mirror/method_handle_impl.cc",
+        "mirror/method_type.cc",
         "mirror/object.cc",
         "mirror/reference.cc",
         "mirror/stack_trace_element.cc",
@@ -151,9 +153,9 @@
         "native/java_lang_VMClassLoader.cc",
         "native/java_lang_ref_FinalizerReference.cc",
         "native/java_lang_ref_Reference.cc",
-        "native/java_lang_reflect_AbstractMethod.cc",
         "native/java_lang_reflect_Array.cc",
         "native/java_lang_reflect_Constructor.cc",
+        "native/java_lang_reflect_Executable.cc",
         "native/java_lang_reflect_Field.cc",
         "native/java_lang_reflect_Method.cc",
         "native/java_lang_reflect_Parameter.cc",
@@ -382,7 +384,8 @@
         "libnativeloader",
         "libbacktrace",
         "liblz4",
-        // For liblog, atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted.
+        "liblog",
+        // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted.
         "libcutils",
         // For common macros.
         "libbase",
@@ -469,6 +472,7 @@
     srcs: ["common_runtime_test.cc"],
     shared_libs: [
         "libartd",
+        "libbase",
     ],
 }
 
@@ -544,6 +548,7 @@
         "mem_map_test.cc",
         "memory_region_test.cc",
         "mirror/dex_cache_test.cc",
+        "mirror/method_type_test.cc",
         "mirror/object_test.cc",
         "monitor_pool_test.cc",
         "monitor_test.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 0b04480..cdb4c25 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -272,6 +272,15 @@
 END \c_name
 .endm
 
+.macro NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING c_name, cxx_name
+    .extern \cxx_name
+ENTRY \c_name
+    SETUP_SAVE_EVERYTHING_FRAME r0  @ save all registers as basis for long jump context
+    mov r0, r9                      @ pass Thread::Current
+    bl  \cxx_name                   @ \cxx_name(Thread*)
+END \c_name
+.endm
+
 .macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
@@ -281,10 +290,10 @@
 END \c_name
 .endm
 
-.macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
+.macro TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2       @ save all registers as basis for long jump context
+    SETUP_SAVE_EVERYTHING_FRAME r2  @ save all registers as basis for long jump context
     mov r2, r9                      @ pass Thread::Current
     bl  \cxx_name                   @ \cxx_name(Thread*)
 END \c_name
@@ -361,7 +370,7 @@
     /*
      * Called by managed code to create and deliver a NullPointerException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 
     /*
      * Call installed by a signal handler to create and deliver a NullPointerException.
@@ -398,19 +407,19 @@
     /*
      * Called by managed code to create and deliver an ArithmeticException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_div_zero, artThrowDivZeroFromCode
 
     /*
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
      * index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StringIndexOutOfBoundsException
      * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThrowStringBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StackOverflowError.
@@ -1701,7 +1710,11 @@
     .cfi_rel_offset lr, 12
     ldr   r3, [r0, #MIRROR_STRING_COUNT_OFFSET]
     add   r0, #MIRROR_STRING_VALUE_OFFSET
-
+#if (STRING_COMPRESSION_FEATURE)
+    /* r4 count (with flag) and r3 holds actual length */
+    mov   r4, r3
+    bic   r3, #2147483648
+#endif
     /* Clamp start to [0..count] */
     cmp   r2, #0
     it    lt
@@ -1714,6 +1727,10 @@
     mov   r12, r0
 
     /* Build pointer to start of data to compare and pre-bias */
+#if (STRING_COMPRESSION_FEATURE)
+    cmp   r4, #0
+    blt   .Lstring_indexof_compressed
+#endif
     add   r0, r0, r2, lsl #1
     sub   r0, #2
 
@@ -1725,6 +1742,7 @@
      *   r0: start of data to test
      *   r1: char to compare
      *   r2: iteration count
+     *   r4: compression style (used temporarily)
      *   r12: original start of string data
      *   r3, r4, r10, r11 available for loading string data
      */
@@ -1782,6 +1800,22 @@
     sub   r0, r12
     asr   r0, r0, #1
     pop {r4, r10-r11, pc}
+#if (STRING_COMPRESSION_FEATURE)
+.Lstring_indexof_compressed:
+    add   r0, r0, r2
+    sub   r0, #1
+    sub   r2, r3, r2
+.Lstring_indexof_compressed_loop:
+    subs  r2, #1
+    blt   .Lindexof_nomatch
+    ldrb  r3, [r0, #1]!
+    cmp   r3, r1
+    beq   .Lstring_indexof_compressed_matched
+    b     .Lstring_indexof_compressed_loop
+.Lstring_indexof_compressed_matched:
+    sub   r0, r12
+    pop {r4, r10-r11, pc}
+#endif
 END art_quick_indexof
 
     /* Assembly routines used to handle ABI differences. */
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index e9d03d7..04a3cc6 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -434,6 +434,17 @@
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
     mov x0, xSELF                     // pass Thread::Current
     bl  \cxx_name                     // \cxx_name(Thread*)
+    brk 0
+END \c_name
+.endm
+
+.macro NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING c_name, cxx_name
+    .extern \cxx_name
+ENTRY \c_name
+    SETUP_SAVE_EVERYTHING_FRAME       // save all registers as basis for long jump context
+    mov x0, xSELF                     // pass Thread::Current
+    bl  \cxx_name                     // \cxx_name(Thread*)
+    brk 0
 END \c_name
 .endm
 
@@ -447,10 +458,10 @@
 END \c_name
 .endm
 
-.macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
+.macro TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING c_name, cxx_name
     .extern \cxx_name
 ENTRY \c_name
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
+    SETUP_SAVE_EVERYTHING_FRAME       // save all registers as basis for long jump context
     mov x2, xSELF                     // pass Thread::Current
     bl  \cxx_name                     // \cxx_name(arg1, arg2, Thread*)
     brk 0
@@ -466,7 +477,7 @@
     /*
      * Called by managed code to create and deliver a NullPointerException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 
     /*
      * Call installed by a signal handler to create and deliver a NullPointerException.
@@ -490,19 +501,19 @@
     /*
      * Called by managed code to create and deliver an ArithmeticException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_div_zero, artThrowDivZeroFromCode
 
     /*
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
      * index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StringIndexOutOfBoundsException
      * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThrowStringBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StackOverflowError.
@@ -2367,7 +2378,11 @@
 ENTRY art_quick_indexof
     ldr   w3, [x0, #MIRROR_STRING_COUNT_OFFSET]
     add   x0, x0, #MIRROR_STRING_VALUE_OFFSET
-
+#if (STRING_COMPRESSION_FEATURE)
+    /* w4 holds count (with flag) and w3 holds actual length */
+    mov   w4, w3
+    and   w3, w3, #2147483647
+#endif
     /* Clamp start to [0..count] */
     cmp   w2, #0
     csel  w2, wzr, w2, lt
@@ -2377,10 +2392,12 @@
     /* Save a copy to compute result */
     mov   x5, x0
 
+#if (STRING_COMPRESSION_FEATURE)
+    tbnz  w4, #31, .Lstring_indexof_compressed
+#endif
     /* Build pointer to start of data to compare and pre-bias */
     add   x0, x0, x2, lsl #1
     sub   x0, x0, #2
-
     /* Compute iteration count */
     sub   w2, w3, w2
 
@@ -2445,6 +2462,26 @@
     sub   x0, x0, x5
     asr   x0, x0, #1
     ret
+#if (STRING_COMPRESSION_FEATURE)
+   /*
+    * Comparing compressed string character-per-character with
+    * input character
+    */
+.Lstring_indexof_compressed:
+    add   x0, x0, x2
+    sub   x0, x0, #1
+    sub   w2, w3, w2
+.Lstring_indexof_compressed_loop:
+    subs  w2, w2, #1
+    b.lt  .Lindexof_nomatch
+    ldrb  w6, [x0, #1]!
+    cmp   w6, w1
+    b.eq  .Lstring_indexof_compressed_matched
+    b     .Lstring_indexof_compressed_loop
+.Lstring_indexof_compressed_matched:
+    sub   x0, x0, x5
+    ret
+#endif
 END art_quick_indexof
 
     /*
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 7bbc709..0b8e531 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -19,7 +19,7 @@
 #include <gtest/gtest.h>
 
 #ifdef ART_TARGET_ANDROID
-#include "cutils/properties.h"
+#include "android-base/properties.h"
 #endif
 
 #include "base/logging.h"
@@ -40,8 +40,8 @@
 
   // Read the variant property.
   std::string key = StringPrintf("dalvik.vm.isa.%s.variant", GetInstructionSetString(kRuntimeISA));
-  char dex2oat_isa_variant[PROPERTY_VALUE_MAX];
-  if (property_get(key.c_str(), dex2oat_isa_variant, nullptr) > 0) {
+  std::string dex2oat_isa_variant = android::base::GetProperty(key, "");
+  if (!dex2oat_isa_variant.empty()) {
     // Use features from property to build InstructionSetFeatures and check against build's
     // features.
     std::string error_msg;
@@ -68,13 +68,13 @@
   // Read the variant property.
   std::string variant_key = StringPrintf("dalvik.vm.isa.%s.variant",
                                          GetInstructionSetString(kRuntimeISA));
-  char dex2oat_isa_variant[PROPERTY_VALUE_MAX];
-  if (property_get(variant_key.c_str(), dex2oat_isa_variant, nullptr) > 0) {
+  std::string dex2oat_isa_variant = android::base::GetProperty(variant_key, "");
+  if (!dex2oat_isa_variant.empty()) {
     // Read the features property.
     std::string features_key = StringPrintf("dalvik.vm.isa.%s.features",
                                             GetInstructionSetString(kRuntimeISA));
-    char dex2oat_isa_features[PROPERTY_VALUE_MAX];
-    if (property_get(features_key.c_str(), dex2oat_isa_features, nullptr) > 0) {
+    std::string dex2oat_isa_features = android::base::GetProperty(features_key, "");
+    if (!dex2oat_isa_features.empty()) {
       // Use features from property to build InstructionSetFeatures and check against build's
       // features.
       std::string error_msg;
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index 7955b1d..948b06c 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -26,22 +26,6 @@
 // Register holding Thread::Current().
 #define rSELF $s1
 
-     // Declare a function called name, sets up $gp.
-.macro ENTRY name
-    .type \name, %function
-    .global \name
-    // Cache alignment for function entry.
-    .balign 16
-\name:
-    .cfi_startproc
-     // Ensure we get a sane starting CFA.
-    .cfi_def_cfa $sp,0
-    // Load $gp. We expect that ".set noreorder" is in effect.
-    .cpload $t9
-    // Declare a local convenience label to be branched to when $gp is already set up.
-.L\name\()_gp_set:
-.endm
-
      // Declare a function called name, doesn't set up $gp.
 .macro ENTRY_NO_GP_CUSTOM_CFA name, cfa_offset
     .type \name, %function
@@ -59,6 +43,15 @@
     ENTRY_NO_GP_CUSTOM_CFA \name, 0
 .endm
 
+     // Declare a function called name, sets up $gp.
+.macro ENTRY name
+    ENTRY_NO_GP \name
+    // Load $gp. We expect that ".set noreorder" is in effect.
+    .cpload $t9
+    // Declare a local convenience label to be branched to when $gp is already set up.
+.L\name\()_gp_set:
+.endm
+
 .macro END name
     .cfi_endproc
     .size \name, .-\name
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index b6a63ca..1792f31 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -90,7 +90,7 @@
 
   sc->sc_regs[mips::RA] = sc->sc_pc + 4;      // RA needs to point to gc map location
   sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
-  sc->sc_regs[mips::T9] = sc->sc_pc;          // make sure T9 points to the function
+  // Note: This entrypoint does not rely on T9 pointing to it, so we may as well preserve T9.
   VLOG(signals) << "Generating null pointer exception";
   return true;
 }
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 4563004..c3c1882 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -710,8 +710,10 @@
      * Called by managed code to create and deliver a NullPointerException
      */
     .extern artThrowNullPointerExceptionFromCode
-ENTRY art_quick_throw_null_pointer_exception
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_null_pointer_exception
+    // Note that setting up $gp does not rely on $t9 here, so branching here directly is OK,
+    // even after clobbering any registers we don't need to preserve, such as $gp or $t0.
+    SETUP_SAVE_EVERYTHING_FRAME
     la   $t9, artThrowNullPointerExceptionFromCode
     jalr $zero, $t9                 # artThrowNullPointerExceptionFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
@@ -735,8 +737,8 @@
      * Called by managed code to create and deliver an ArithmeticException
      */
     .extern artThrowDivZeroFromCode
-ENTRY art_quick_throw_div_zero
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_div_zero
+    SETUP_SAVE_EVERYTHING_FRAME
     la   $t9, artThrowDivZeroFromCode
     jalr $zero, $t9                 # artThrowDivZeroFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
@@ -746,8 +748,10 @@
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException
      */
     .extern artThrowArrayBoundsFromCode
-ENTRY art_quick_throw_array_bounds
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_array_bounds
+    // Note that setting up $gp does not rely on $t9 here, so branching here directly is OK,
+    // even after clobbering any registers we don't need to preserve, such as $gp or $t0.
+    SETUP_SAVE_EVERYTHING_FRAME
     la   $t9, artThrowArrayBoundsFromCode
     jalr $zero, $t9                 # artThrowArrayBoundsFromCode(index, limit, Thread*)
     move $a2, rSELF                 # pass Thread::Current
@@ -758,8 +762,8 @@
      * as if thrown from a call to String.charAt().
      */
     .extern artThrowStringBoundsFromCode
-ENTRY art_quick_throw_string_bounds
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_string_bounds
+    SETUP_SAVE_EVERYTHING_FRAME
     la   $t9, artThrowStringBoundsFromCode
     jalr $zero, $t9                 # artThrowStringBoundsFromCode(index, limit, Thread*)
     move $a2, rSELF                 # pass Thread::Current
@@ -1123,7 +1127,7 @@
      */
     .extern artLockObjectFromCode
 ENTRY art_quick_lock_object
-    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, art_quick_throw_null_pointer_exception
     nop
     SETUP_SAVE_REFS_ONLY_FRAME            # save callee saves in case we block
     la      $t9, artLockObjectFromCode
@@ -1133,7 +1137,7 @@
 END art_quick_lock_object
 
 ENTRY art_quick_lock_object_no_inline
-    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, art_quick_throw_null_pointer_exception
     nop
     SETUP_SAVE_REFS_ONLY_FRAME            # save callee saves in case we block
     la      $t9, artLockObjectFromCode
@@ -1147,7 +1151,7 @@
      */
     .extern artUnlockObjectFromCode
 ENTRY art_quick_unlock_object
-    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, art_quick_throw_null_pointer_exception
     nop
     SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case exception allocation triggers GC
     la      $t9, artUnlockObjectFromCode
@@ -1157,7 +1161,7 @@
 END art_quick_unlock_object
 
 ENTRY art_quick_unlock_object_no_inline
-    beqz    $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+    beqz    $a0, art_quick_throw_null_pointer_exception
     nop
     SETUP_SAVE_REFS_ONLY_FRAME        # save callee saves in case exception allocation triggers GC
     la      $t9, artUnlockObjectFromCode
@@ -1280,7 +1284,7 @@
 ENTRY art_quick_aput_obj_with_null_and_bound_check
     bnez    $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
     nop
-    b .Lart_quick_throw_null_pointer_exception_gp_set
+    b art_quick_throw_null_pointer_exception
     nop
 END art_quick_aput_obj_with_null_and_bound_check
 
@@ -1290,7 +1294,7 @@
     bnez $t1, .Lart_quick_aput_obj_gp_set
     nop
     move $a0, $a1
-    b .Lart_quick_throw_array_bounds_gp_set
+    b art_quick_throw_array_bounds
     move $a1, $t0
 END art_quick_aput_obj_with_bound_check
 
diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S
index 6c58fcf..35f20fb 100644
--- a/runtime/arch/mips64/asm_support_mips64.S
+++ b/runtime/arch/mips64/asm_support_mips64.S
@@ -27,24 +27,6 @@
 #define rSELF $s1
 
 
-    // Declare a function called name, sets up $gp.
-    // This macro modifies t8.
-.macro ENTRY name
-    .type \name, %function
-    .global \name
-    // Cache alignment for function entry.
-    .balign 16
-\name:
-    .cfi_startproc
-    // Set up $gp and store the previous $gp value to $t8. It will be pushed to the
-    // stack after the frame has been constructed.
-    .cpsetup $t9, $t8, \name
-    // Ensure we get a sane starting CFA.
-    .cfi_def_cfa $sp,0
-    // Declare a local convenience label to be branched to when $gp is already set up.
-.L\name\()_gp_set:
-.endm
-
     // Declare a function called name, doesn't set up $gp.
 .macro ENTRY_NO_GP_CUSTOM_CFA name, cfa_offset
     .type \name, %function
@@ -62,6 +44,17 @@
     ENTRY_NO_GP_CUSTOM_CFA \name, 0
 .endm
 
+    // Declare a function called name, sets up $gp.
+    // This macro modifies t8.
+.macro ENTRY name
+    ENTRY_NO_GP \name
+    // Set up $gp and store the previous $gp value to $t8. It will be pushed to the
+    // stack after the frame has been constructed.
+    .cpsetup $t9, $t8, \name
+    // Declare a local convenience label to be branched to when $gp is already set up.
+.L\name\()_gp_set:
+.endm
+
 .macro END name
     .cfi_endproc
     .size \name, .-\name
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
index e52dc73..709cab5 100644
--- a/runtime/arch/mips64/fault_handler_mips64.cc
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -91,7 +91,7 @@
 
   sc->sc_regs[mips64::RA] = sc->sc_pc + 4;      // RA needs to point to gc map location
   sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
-  sc->sc_regs[mips64::T9] = sc->sc_pc;          // make sure T9 points to the function
+  // Note: This entrypoint does not rely on T9 pointing to it, so we may as well preserve T9.
   VLOG(signals) << "Generating null pointer exception";
   return true;
 }
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index c16e855..8fc7bc3 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -817,9 +817,10 @@
      * Called by managed code to create and deliver a NullPointerException
      */
     .extern artThrowNullPointerExceptionFromCode
-ENTRY art_quick_throw_null_pointer_exception
-.Lart_quick_throw_null_pointer_exception_gp_set:
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_null_pointer_exception
+    // Note that setting up $gp does not rely on $t9 here, so branching here directly is OK,
+    // even after clobbering any registers we don't need to preserve, such as $gp or $t0.
+    SETUP_SAVE_EVERYTHING_FRAME
     dla  $t9, artThrowNullPointerExceptionFromCode
     jalr $zero, $t9                 # artThrowNullPointerExceptionFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
@@ -842,8 +843,8 @@
      * Called by managed code to create and deliver an ArithmeticException
      */
     .extern artThrowDivZeroFromCode
-ENTRY art_quick_throw_div_zero
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_div_zero
+    SETUP_SAVE_EVERYTHING_FRAME
     dla  $t9, artThrowDivZeroFromCode
     jalr $zero, $t9                 # artThrowDivZeroFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
@@ -854,9 +855,10 @@
      * ArrayIndexOutOfBoundsException
      */
     .extern artThrowArrayBoundsFromCode
-ENTRY art_quick_throw_array_bounds
-.Lart_quick_throw_array_bounds_gp_set:
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_array_bounds
+    // Note that setting up $gp does not rely on $t9 here, so branching here directly is OK,
+    // even after clobbering any registers we don't need to preserve, such as $gp or $t0.
+    SETUP_SAVE_EVERYTHING_FRAME
     dla  $t9, artThrowArrayBoundsFromCode
     jalr $zero, $t9                 # artThrowArrayBoundsFromCode(index, limit, Thread*)
     move $a2, rSELF                 # pass Thread::Current
@@ -867,9 +869,8 @@
      * as if thrown from a call to String.charAt().
      */
     .extern artThrowStringBoundsFromCode
-ENTRY art_quick_throw_string_bounds
-.Lart_quick_throw_string_bounds_gp_set:
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_throw_string_bounds
+    SETUP_SAVE_EVERYTHING_FRAME
     dla  $t9, artThrowStringBoundsFromCode
     jalr $zero, $t9                 # artThrowStringBoundsFromCode(index, limit, Thread*)
     move $a2, rSELF                 # pass Thread::Current
@@ -1210,18 +1211,20 @@
      * Entry from managed code that calls artLockObjectFromCode, may block for GC.
      */
     .extern artLockObjectFromCode
-ENTRY art_quick_lock_object
-    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ENTRY_NO_GP art_quick_lock_object
+    beq     $a0, $zero, art_quick_throw_null_pointer_exception
     nop
+    .cpsetup $t9, $t8, art_quick_lock_object
     SETUP_SAVE_REFS_ONLY_FRAME            # save callee saves in case we block
     jal     artLockObjectFromCode         # (Object* obj, Thread*)
     move    $a1, rSELF                    # pass Thread::Current
     RETURN_IF_ZERO
 END art_quick_lock_object
 
-ENTRY art_quick_lock_object_no_inline
-    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ENTRY_NO_GP art_quick_lock_object_no_inline
+    beq     $a0, $zero, art_quick_throw_null_pointer_exception
     nop
+    .cpsetup $t9, $t8, art_quick_lock_object_no_inline
     SETUP_SAVE_REFS_ONLY_FRAME            # save callee saves in case we block
     jal     artLockObjectFromCode         # (Object* obj, Thread*)
     move    $a1, rSELF                    # pass Thread::Current
@@ -1232,18 +1235,20 @@
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      */
     .extern artUnlockObjectFromCode
-ENTRY art_quick_unlock_object
-    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ENTRY_NO_GP art_quick_unlock_object
+    beq     $a0, $zero, art_quick_throw_null_pointer_exception
     nop
+    .cpsetup $t9, $t8, art_quick_unlock_object
     SETUP_SAVE_REFS_ONLY_FRAME         # save callee saves in case exception allocation triggers GC
     jal     artUnlockObjectFromCode    # (Object* obj, Thread*)
     move    $a1, rSELF                 # pass Thread::Current
     RETURN_IF_ZERO
 END art_quick_unlock_object
 
-ENTRY art_quick_unlock_object_no_inline
-    beq     $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ENTRY_NO_GP art_quick_unlock_object_no_inline
+    beq     $a0, $zero, art_quick_throw_null_pointer_exception
     nop
+    .cpsetup $t9, $t8, art_quick_unlock_object_no_inline
     SETUP_SAVE_REFS_ONLY_FRAME         # save callee saves in case exception allocation triggers GC
     jal     artUnlockObjectFromCode    # (Object* obj, Thread*)
     move    $a1, rSELF                 # pass Thread::Current
@@ -1362,7 +1367,7 @@
 ENTRY art_quick_aput_obj_with_null_and_bound_check
     bne    $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set
     nop
-    b .Lart_quick_throw_null_pointer_exception_gp_set
+    b art_quick_throw_null_pointer_exception
     nop
 END art_quick_aput_obj_with_null_and_bound_check
 
@@ -1372,7 +1377,7 @@
     bne  $t1, $zero, .Lart_quick_aput_obj_gp_set
     nop
     move $a0, $a1
-    b .Lart_quick_throw_array_bounds_gp_set
+    b art_quick_throw_array_bounds
     move $a1, $t0
 END art_quick_aput_obj_with_bound_check
 
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 507dbf0..5e39f42 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -22,10 +22,11 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "imt_conflict_table.h"
 #include "linear_alloc.h"
 #include "mirror/class-inl.h"
 #include "mirror/string-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -1795,7 +1796,7 @@
 
   ScopedObjectAccess soa(self);
   StackHandleScope<3> hs(self);
-  Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(o)));
+  Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object>(o)));
   Handle<mirror::Class> c(hs.NewHandle(obj->GetClass()));
   // Need a method as a referrer
   ArtMethod* m = c->GetDirectMethod(0, kRuntimePointerSize);
@@ -1994,11 +1995,11 @@
 
   jobject jarray_list = env->NewObject(arraylist_jclass, arraylist_constructor);
   ASSERT_NE(nullptr, jarray_list);
-  Handle<mirror::Object> array_list(hs.NewHandle(soa.Decode<mirror::Object*>(jarray_list)));
+  Handle<mirror::Object> array_list(hs.NewHandle(soa.Decode<mirror::Object>(jarray_list)));
 
   jobject jobj = env->NewObject(obj_jclass, obj_constructor);
   ASSERT_NE(nullptr, jobj);
-  Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(jobj)));
+  Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object>(jobj)));
 
   // Invocation tests.
 
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index f3793e1..7bb59ef 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -327,6 +327,19 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
+MACRO2(NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
+    DEFINE_FUNCTION VAR(c_name)
+    SETUP_SAVE_EVERYTHING_FRAME ebx, ebx       // save all registers as basis for long jump context
+    // Outgoing argument set up
+    subl MACRO_LITERAL(12), %esp               // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    pushl %fs:THREAD_SELF_OFFSET               // pass Thread::Current()
+    CFI_ADJUST_CFA_OFFSET(4)
+    call CALLVAR(cxx_name)                     // cxx_name(Thread*)
+    UNREACHABLE
+    END_FUNCTION VAR(c_name)
+END_MACRO
+
 MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context
@@ -341,9 +354,9 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
+MACRO2(TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
     DEFINE_FUNCTION VAR(c_name)
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context
+    SETUP_SAVE_EVERYTHING_FRAME ebx, ebx       // save all registers as basis for long jump context
     // Outgoing argument set up
     PUSH eax                                   // alignment padding
     pushl %fs:THREAD_SELF_OFFSET               // pass Thread::Current()
@@ -358,7 +371,7 @@
     /*
      * Called by managed code to create and deliver a NullPointerException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 
     /*
      * Call installed by a signal handler to create and deliver a NullPointerException.
@@ -384,7 +397,7 @@
     /*
      * Called by managed code to create and deliver an ArithmeticException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_div_zero, artThrowDivZeroFromCode
 
     /*
      * Called by managed code to create and deliver a StackOverflowError.
@@ -401,13 +414,13 @@
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
      * index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StringIndexOutOfBoundsException
      * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThrowStringBoundsFromCode
 
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
@@ -1973,11 +1986,70 @@
     mov MIRROR_STRING_COUNT_OFFSET(%ecx), %ebx
     lea MIRROR_STRING_VALUE_OFFSET(%eax), %esi
     lea MIRROR_STRING_VALUE_OFFSET(%ecx), %edi
+#if (STRING_COMPRESSION_FEATURE)
+    /* Differ cases */
+    cmpl    LITERAL(0), %edx
+    jl      .Lstring_compareto_this_is_compressed
+    cmpl    LITERAL(0), %ebx
+    jl      .Lstring_compareto_that_is_compressed
+    jmp     .Lstring_compareto_both_not_compressed
+.Lstring_compareto_this_is_compressed:
+    andl    LITERAL(0x7FFFFFFF), %edx
+    cmpl    LITERAL(0), %ebx
+    jl      .Lstring_compareto_both_compressed
+    /* If (this->IsCompressed() && that->IsCompressed() == false) */
+    mov     %edx, %eax
+    subl    %ebx, %eax
+    mov     %edx, %ecx
+    cmovg   %ebx, %ecx
+    /* Going into loop to compare each character */
+    jecxz   .Lstring_compareto_keep_length            // check loop counter (if 0, don't compare)
+.Lstring_compareto_loop_comparison_this_compressed:
+    movzbl  (%esi), %edx                              // move *(this_cur_char) byte to long
+    movzwl  (%edi), %ebx                              // move *(that_cur_char) word to long
+    addl    LITERAL(1), %esi                          // ++this_cur_char (8-bit)
+    addl    LITERAL(2), %edi                          // ++that_cur_char (16-bit)
+    subl    %ebx, %edx
+    loope   .Lstring_compareto_loop_comparison_this_compressed
+    cmovne  %edx, %eax                        // return eax = *(this_cur_char) - *(that_cur_char)
+    jmp     .Lstring_compareto_return
+.Lstring_compareto_that_is_compressed:
+    andl    LITERAL(0x7FFFFFFF), %ebx
+    mov     %edx, %eax
+    subl    %ebx, %eax
+    mov     %edx, %ecx
+    cmovg   %ebx, %ecx
+    /* If (this->IsCompressed() == false && that->IsCompressed()) */
+    jecxz   .Lstring_compareto_keep_length            // check loop counter, if 0, don't compare
+.Lstring_compareto_loop_comparison_that_compressed:
+    movzwl  (%esi), %edx                              // move *(this_cur_char) word to long
+    movzbl  (%edi), %ebx                              // move *(that_cur_char) byte to long
+    addl    LITERAL(2), %esi                          // ++this_cur_char (16-bit)
+    addl    LITERAL(1), %edi                          // ++that_cur_char (8-bit)
+    subl    %ebx, %edx
+    loope   .Lstring_compareto_loop_comparison_that_compressed
+    cmovne  %edx, %eax
+    jmp     .Lstring_compareto_return         // return eax = *(this_cur_char) - *(that_cur_char)
+.Lstring_compareto_both_compressed:
+    andl    LITERAL(0x7FFFFFFF), %ebx
     /* Calculate min length and count diff */
-    mov   %edx, %ecx
-    mov   %edx, %eax
-    subl  %ebx, %eax
-    cmovg %ebx, %ecx
+    mov     %edx, %ecx
+    mov     %edx, %eax
+    subl    %ebx, %eax
+    cmovg   %ebx, %ecx
+    jecxz   .Lstring_compareto_keep_length
+    repe    cmpsb
+    je      .Lstring_compareto_keep_length
+    movzbl  -1(%esi), %eax        // get last compared char from this string (8-bit)
+    movzbl  -1(%edi), %ecx        // get last compared char from comp string (8-bit)
+    jmp     .Lstring_compareto_count_difference
+#endif // STRING_COMPRESSION_FEATURE
+.Lstring_compareto_both_not_compressed:
+    /* Calculate min length and count diff */
+    mov     %edx, %ecx
+    mov     %edx, %eax
+    subl    %ebx, %eax
+    cmovg   %ebx, %ecx
     /*
      * At this point we have:
      *   eax: value to return if first part of strings are equal
@@ -1985,18 +2057,15 @@
      *   esi: pointer to this string data
      *   edi: pointer to comp string data
      */
-    jecxz .Lkeep_length
-    repe cmpsw                    // find nonmatching chars in [%esi] and [%edi], up to length %ecx
-    jne .Lnot_equal
-.Lkeep_length:
-    POP edi                       // pop callee save reg
-    POP esi                       // pop callee save reg
-    ret
-    .balign 16
-.Lnot_equal:
-    movzwl  -2(%esi), %eax        // get last compared char from this string
-    movzwl  -2(%edi), %ecx        // get last compared char from comp string
-    subl  %ecx, %eax              // return the difference
+    jecxz .Lstring_compareto_keep_length
+    repe  cmpsw                   // find nonmatching chars in [%esi] and [%edi], up to length %ecx
+    je    .Lstring_compareto_keep_length
+    movzwl  -2(%esi), %eax        // get last compared char from this string (16-bit)
+    movzwl  -2(%edi), %ecx        // get last compared char from comp string (16-bit)
+.Lstring_compareto_count_difference:
+    subl    %ecx, %eax
+.Lstring_compareto_keep_length:
+.Lstring_compareto_return:
     POP edi                       // pop callee save reg
     POP esi                       // pop callee save reg
     ret
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index bfba543..54e52e5 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -395,6 +395,16 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
+MACRO2(NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
+    DEFINE_FUNCTION VAR(c_name)
+    SETUP_SAVE_EVERYTHING_FRAME        // save all registers as basis for long jump context
+    // Outgoing argument set up
+    movq %gs:THREAD_SELF_OFFSET, %rdi  // pass Thread::Current()
+    call CALLVAR(cxx_name)             // cxx_name(Thread*)
+    UNREACHABLE
+    END_FUNCTION VAR(c_name)
+END_MACRO
+
 MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME  // save all registers as basis for long jump context
@@ -405,9 +415,9 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
+MACRO2(TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING, c_name, cxx_name)
     DEFINE_FUNCTION VAR(c_name)
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME  // save all registers as basis for long jump context
+    SETUP_SAVE_EVERYTHING_FRAME        // save all registers as basis for long jump context
     // Outgoing argument set up
     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
     call CALLVAR(cxx_name)             // cxx_name(Thread*)
@@ -418,7 +428,7 @@
     /*
      * Called by managed code to create and deliver a NullPointerException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 
     /*
      * Call installed by a signal handler to create and deliver a NullPointerException.
@@ -440,7 +450,7 @@
     /*
      * Called by managed code to create and deliver an ArithmeticException.
      */
-NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
+NO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_div_zero, artThrowDivZeroFromCode
 
     /*
      * Called by managed code to create and deliver a StackOverflowError.
@@ -457,13 +467,13 @@
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
      * index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 
     /*
      * Called by managed code to create and deliver a StringIndexOutOfBoundsException
      * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
      */
-TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThrowStringBoundsFromCode
 
     /*
      * All generated callsites for interface invokes and invocation slow paths will load arguments
@@ -2090,11 +2100,70 @@
     /* Build pointers to the start of string data */
     leal MIRROR_STRING_VALUE_OFFSET(%edi), %edi
     leal MIRROR_STRING_VALUE_OFFSET(%esi), %esi
+#if (STRING_COMPRESSION_FEATURE)
+    /* Differ cases */
+    cmpl LITERAL(0), %r8d
+    jl      .Lstring_compareto_this_is_compressed
+    cmpl    LITERAL(0), %r9d
+    jl      .Lstring_compareto_that_is_compressed
+    jmp     .Lstring_compareto_both_not_compressed
+.Lstring_compareto_this_is_compressed:
+    andl    LITERAL(0x7FFFFFFF), %r8d
+    cmpl    LITERAL(0), %r9d
+    jl      .Lstring_compareto_both_compressed
+    /* Comparison this (8-bit) and that (16-bit) */
+    mov     %r8d, %eax
+    subl    %r9d, %eax
+    mov     %r8d, %ecx
+    cmovg   %r9d, %ecx
+    /* Going into loop to compare each character */
+    jecxz   .Lstring_compareto_keep_length      // check loop counter (if 0 then stop)
+.Lstring_compareto_loop_comparison_this_compressed:
+    movzbl  (%edi), %r8d                        // move *(this_cur_char) byte to long
+    movzwl  (%esi), %r9d                        // move *(that_cur_char) word to long
+    addl    LITERAL(1), %edi                    // ++this_cur_char (8-bit)
+    addl    LITERAL(2), %esi                    // ++that_cur_char (16-bit)
+    subl    %r9d, %r8d
+    loope   .Lstring_compareto_loop_comparison_this_compressed
+    cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
+    ret
+.Lstring_compareto_that_is_compressed:
+    andl    LITERAL(0x7FFFFFFF), %r9d
+    movl    %r8d, %eax
+    subl    %r9d, %eax
+    mov     %r8d, %ecx
+    cmovg   %r9d, %ecx
+    /* Comparison this (8-bit) and that (16-bit) */
+    jecxz   .Lstring_compareto_keep_length      // check loop counter (if 0, don't compare)
+.Lstring_compareto_loop_comparison_that_compressed:
+    movzwl  (%edi), %r8d                        // move *(this_cur_char) word to long
+    movzbl  (%esi), %r9d                        // move *(that_cur_chat) byte to long
+    addl    LITERAL(2), %edi                    // ++this_cur_char (16-bit)
+    addl    LITERAL(1), %esi                    // ++that_cur_char (8-bit)
+    subl    %r9d, %r8d
+    loope   .Lstring_compareto_loop_comparison_that_compressed
+    cmovne  %r8d, %eax                          // return eax = *(this_cur_char) - *(that_cur_char)
+    ret
+.Lstring_compareto_both_compressed:
+    andl    LITERAL(0x7FFFFFFF), %r9d
     /* Calculate min length and count diff */
-    movl  %r8d, %ecx
-    movl  %r8d, %eax
-    subl  %r9d, %eax
-    cmovg %r9d, %ecx
+    movl    %r8d, %ecx
+    movl    %r8d, %eax
+    subl    %r9d, %eax
+    cmovg   %r9d, %ecx
+    jecxz   .Lstring_compareto_keep_length
+    repe    cmpsb
+    je      .Lstring_compareto_keep_length
+    movzbl  -1(%edi), %eax        // get last compared char from this string (8-bit)
+    movzbl  -1(%esi), %ecx        // get last compared char from comp string (8-bit)
+    jmp     .Lstring_compareto_count_difference
+#endif // STRING_COMPRESSION_FEATURE
+.Lstring_compareto_both_not_compressed:
+    /* Calculate min length and count diff */
+    movl    %r8d, %ecx
+    movl    %r8d, %eax
+    subl    %r9d, %eax
+    cmovg   %r9d, %ecx
     /*
      * At this point we have:
      *   eax: value to return if first part of strings are equal
@@ -2102,16 +2171,14 @@
      *   esi: pointer to comp string data
      *   edi: pointer to this string data
      */
-    jecxz .Lkeep_length
-    repe cmpsw                    // find nonmatching chars in [%esi] and [%edi], up to length %ecx
-    jne .Lnot_equal
-.Lkeep_length:
-    ret
-    .balign 16
-.Lnot_equal:
-    movzwl  -2(%edi), %eax        // get last compared char from this string
-    movzwl  -2(%esi), %ecx        // get last compared char from comp string
+    jecxz .Lstring_compareto_keep_length
+    repe  cmpsw                   // find nonmatching chars in [%esi] and [%edi], up to length %ecx
+    je    .Lstring_compareto_keep_length
+    movzwl  -2(%edi), %eax        // get last compared char from this string (16-bit)
+    movzwl  -2(%esi), %ecx        // get last compared char from comp string (16-bit)
+.Lstring_compareto_count_difference:
     subl  %ecx, %eax              // return the difference
+.Lstring_compareto_keep_length:
     ret
 END_FUNCTION art_quick_string_compareto
 
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index ef75f94..ca96169 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -28,7 +28,7 @@
 #include "mirror/object-inl.h"
 #include "primitive.h"
 #include "thread-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index ea5078e..3b4db0b 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -24,7 +24,7 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utils.h"
 #include "well_known_classes.h"
 
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9b4b38a..73c6cf1 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -36,7 +36,7 @@
 #include "quick/quick_method_frame_info.h"
 #include "read_barrier-inl.h"
 #include "runtime-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
 
@@ -123,10 +123,6 @@
   return dex_method_index_;
 }
 
-inline uint32_t ArtMethod::GetImtIndex() {
-  return GetDexMethodIndex() % ImTable::kSize;
-}
-
 inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
   return GetNativePointer<ArtMethod**>(DexCacheResolvedMethodsOffset(pointer_size),
                                        pointer_size);
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index fd6c37a..c97c328 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -34,13 +34,13 @@
 #include "jit/jit_code_cache.h"
 #include "jit/profiling_info.h"
 #include "jni_internal.h"
-#include "mirror/abstract_method.h"
 #include "mirror/class-inl.h"
+#include "mirror/executable.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
 #include "oat_file-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -52,9 +52,9 @@
 
 ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
                                           jobject jlr_method) {
-  auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(jlr_method);
-  DCHECK(abstract_method != nullptr);
-  return abstract_method->GetArtMethod();
+  ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(jlr_method);
+  DCHECK(executable != nullptr);
+  return executable->GetArtMethod();
 }
 
 mirror::String* ArtMethod::GetNameAsString(Thread* self) {
@@ -350,7 +350,7 @@
   ScopedObjectAccess soa(self);
   StackHandleScope<1> shs(self);
 
-  mirror::Class* annotation = soa.Decode<mirror::Class*>(klass);
+  ObjPtr<mirror::Class> annotation = soa.Decode<mirror::Class>(klass);
   DCHECK(annotation->IsAnnotation());
   Handle<mirror::Class> annotation_handle(shs.NewHandle(annotation));
 
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b1baccd..7a8f479 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -34,6 +34,7 @@
 namespace art {
 
 template<class T> class Handle;
+class ImtConflictTable;
 union JValue;
 class OatQuickMethodHeader;
 class ProfilingInfo;
@@ -48,175 +49,6 @@
 class PointerArray;
 }  // namespace mirror
 
-// Table to resolve IMT conflicts at runtime. The table is attached to
-// the jni entrypoint of IMT conflict ArtMethods.
-// The table contains a list of pairs of { interface_method, implementation_method }
-// with the last entry being null to make an assembly implementation of a lookup
-// faster.
-class ImtConflictTable {
-  enum MethodIndex {
-    kMethodInterface,
-    kMethodImplementation,
-    kMethodCount,  // Number of elements in enum.
-  };
-
- public:
-  // Build a new table copying `other` and adding the new entry formed of
-  // the pair { `interface_method`, `implementation_method` }
-  ImtConflictTable(ImtConflictTable* other,
-                   ArtMethod* interface_method,
-                   ArtMethod* implementation_method,
-                   PointerSize pointer_size) {
-    const size_t count = other->NumEntries(pointer_size);
-    for (size_t i = 0; i < count; ++i) {
-      SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
-      SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
-    }
-    SetInterfaceMethod(count, pointer_size, interface_method);
-    SetImplementationMethod(count, pointer_size, implementation_method);
-    // Add the null marker.
-    SetInterfaceMethod(count + 1, pointer_size, nullptr);
-    SetImplementationMethod(count + 1, pointer_size, nullptr);
-  }
-
-  // num_entries excludes the header.
-  ImtConflictTable(size_t num_entries, PointerSize pointer_size) {
-    SetInterfaceMethod(num_entries, pointer_size, nullptr);
-    SetImplementationMethod(num_entries, pointer_size, nullptr);
-  }
-
-  // Set an entry at an index.
-  void SetInterfaceMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
-    SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
-  }
-
-  void SetImplementationMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
-    SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
-  }
-
-  ArtMethod* GetInterfaceMethod(size_t index, PointerSize pointer_size) const {
-    return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
-  }
-
-  ArtMethod* GetImplementationMethod(size_t index, PointerSize pointer_size) const {
-    return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
-  }
-
-  // Return true if two conflict tables are the same.
-  bool Equals(ImtConflictTable* other, PointerSize pointer_size) const {
-    size_t num = NumEntries(pointer_size);
-    if (num != other->NumEntries(pointer_size)) {
-      return false;
-    }
-    for (size_t i = 0; i < num; ++i) {
-      if (GetInterfaceMethod(i, pointer_size) != other->GetInterfaceMethod(i, pointer_size) ||
-          GetImplementationMethod(i, pointer_size) !=
-              other->GetImplementationMethod(i, pointer_size)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  // Visit all of the entries.
-  // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
-  // and also returns one. The order is <interface, implementation>.
-  template<typename Visitor>
-  void Visit(const Visitor& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS {
-    uint32_t table_index = 0;
-    for (;;) {
-      ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
-      if (interface_method == nullptr) {
-        break;
-      }
-      ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
-      auto input = std::make_pair(interface_method, implementation_method);
-      std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
-      if (input.first != updated.first) {
-        SetInterfaceMethod(table_index, pointer_size, updated.first);
-      }
-      if (input.second != updated.second) {
-        SetImplementationMethod(table_index, pointer_size, updated.second);
-      }
-      ++table_index;
-    }
-  }
-
-  // Lookup the implementation ArtMethod associated to `interface_method`. Return null
-  // if not found.
-  ArtMethod* Lookup(ArtMethod* interface_method, PointerSize pointer_size) const {
-    uint32_t table_index = 0;
-    for (;;) {
-      ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
-      if (current_interface_method == nullptr) {
-        break;
-      }
-      if (current_interface_method == interface_method) {
-        return GetImplementationMethod(table_index, pointer_size);
-      }
-      ++table_index;
-    }
-    return nullptr;
-  }
-
-  // Compute the number of entries in this table.
-  size_t NumEntries(PointerSize pointer_size) const {
-    uint32_t table_index = 0;
-    while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
-      ++table_index;
-    }
-    return table_index;
-  }
-
-  // Compute the size in bytes taken by this table.
-  size_t ComputeSize(PointerSize pointer_size) const {
-    // Add the end marker.
-    return ComputeSize(NumEntries(pointer_size), pointer_size);
-  }
-
-  // Compute the size in bytes needed for copying the given `table` and add
-  // one more entry.
-  static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, PointerSize pointer_size) {
-    return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
-  }
-
-  // Compute size with a fixed number of entries.
-  static size_t ComputeSize(size_t num_entries, PointerSize pointer_size) {
-    return (num_entries + 1) * EntrySize(pointer_size);  // Add one for null terminator.
-  }
-
-  static size_t EntrySize(PointerSize pointer_size) {
-    return static_cast<size_t>(pointer_size) * static_cast<size_t>(kMethodCount);
-  }
-
- private:
-  ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const {
-    if (pointer_size == PointerSize::k64) {
-      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index]));
-    } else {
-      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index]));
-    }
-  }
-
-  void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
-    if (pointer_size == PointerSize::k64) {
-      data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method));
-    } else {
-      data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method));
-    }
-  }
-
-  // Array of entries that the assembly stubs will iterate over. Note that this is
-  // not fixed size, and we allocate data prior to calling the constructor
-  // of ImtConflictTable.
-  union {
-    uint32_t data32_[0];
-    uint64_t data64_[0];
-  };
-
-  DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
-};
-
 class ArtMethod FINAL {
  public:
   ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
@@ -305,7 +137,52 @@
     return (GetAccessFlags() & kAccFinal) != 0;
   }
 
+  bool IsIntrinsic() {
+    return (GetAccessFlags() & kAccIntrinsic) != 0;
+  }
+
+  void SetIntrinsic(uint32_t intrinsic) {
+    DCHECK(IsUint<8>(intrinsic));
+    uint32_t new_value = (GetAccessFlags() & kAccFlagsNotUsedByIntrinsic) |
+        kAccIntrinsic |
+        (intrinsic << POPCOUNT(kAccFlagsNotUsedByIntrinsic));
+    if (kIsDebugBuild) {
+      uint32_t java_flags = (GetAccessFlags() & kAccJavaFlagsMask);
+      bool is_constructor = IsConstructor();
+      bool is_synchronized = IsSynchronized();
+      bool skip_access_checks = SkipAccessChecks();
+      bool is_fast_native = IsFastNative();
+      bool is_copied = IsCopied();
+      bool is_miranda = IsMiranda();
+      bool is_default = IsDefault();
+      bool is_default_conflict = IsDefaultConflicting();
+      bool is_compilable = IsCompilable();
+      bool must_count_locks = MustCountLocks();
+      SetAccessFlags(new_value);
+      DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask));
+      DCHECK_EQ(is_constructor, IsConstructor());
+      DCHECK_EQ(is_synchronized, IsSynchronized());
+      DCHECK_EQ(skip_access_checks, SkipAccessChecks());
+      DCHECK_EQ(is_fast_native, IsFastNative());
+      DCHECK_EQ(is_copied, IsCopied());
+      DCHECK_EQ(is_miranda, IsMiranda());
+      DCHECK_EQ(is_default, IsDefault());
+      DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
+      DCHECK_EQ(is_compilable, IsCompilable());
+      DCHECK_EQ(must_count_locks, MustCountLocks());
+    } else {
+      SetAccessFlags(new_value);
+    }
+  }
+
+  uint32_t GetIntrinsic() {
+    DCHECK(IsIntrinsic());
+    return (GetAccessFlags() >> POPCOUNT(kAccFlagsNotUsedByIntrinsic)) & kAccMaxIntrinsic;
+  }
+
   bool IsCopied() {
+    static_assert((kAccCopied & kAccFlagsNotUsedByIntrinsic) == kAccCopied,
+                  "kAccCopied conflicts with intrinsic modifier");
     const bool copied = (GetAccessFlags() & kAccCopied) != 0;
     // (IsMiranda() || IsDefaultConflicting()) implies copied
     DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
@@ -314,6 +191,8 @@
   }
 
   bool IsMiranda() {
+    static_assert((kAccMiranda & kAccFlagsNotUsedByIntrinsic) == kAccMiranda,
+                  "kAccMiranda conflicts with intrinsic modifier");
     return (GetAccessFlags() & kAccMiranda) != 0;
   }
 
@@ -324,6 +203,9 @@
   }
 
   bool IsCompilable() {
+    if (IsIntrinsic()) {
+      return true;
+    }
     return (GetAccessFlags() & kAccCompileDontBother) == 0;
   }
 
@@ -331,11 +213,16 @@
   // multiple default methods. It cannot be invoked, throwing an IncompatibleClassChangeError if one
   // attempts to do so.
   bool IsDefaultConflicting() {
+    if (IsIntrinsic()) {
+      return false;
+    }
     return (GetAccessFlags() & kAccDefaultConflict) != 0u;
   }
 
   // This is set by the class linker.
   bool IsDefault() {
+    static_assert((kAccDefault & kAccFlagsNotUsedByIntrinsic) == kAccDefault,
+                  "kAccDefault conflicts with intrinsic modifier");
     return (GetAccessFlags() & kAccDefault) != 0;
   }
 
@@ -372,6 +259,9 @@
   // Should this method be run in the interpreter and count locks (e.g., failed structured-
   // locking verification)?
   bool MustCountLocks() {
+    if (IsIntrinsic()) {
+      return false;
+    }
     return (GetAccessFlags() & kAccMustCountLocks) != 0;
   }
 
@@ -428,8 +318,6 @@
 
   ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ALWAYS_INLINE uint32_t GetImtIndex() REQUIRES_SHARED(Locks::mutator_lock_);
-
   void SetDexMethodIndex(uint32_t new_idx) {
     // Not called within a transaction.
     dex_method_index_ = new_idx;
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b8f7272..567791e 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -240,7 +240,9 @@
 #define MIRROR_STRING_VALUE_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
 
-
+// String compression feature.
+#define STRING_COMPRESSION_FEATURE 0
+ADD_TEST_EQ(STRING_COMPRESSION_FEATURE, art::mirror::kUseStringCompression);
 
 #if defined(__cplusplus)
 }  // End of CheckAsmSupportOffsets.
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index d2f0fdb..f0811b0 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -21,13 +21,8 @@
 #include <limits>
 #include <type_traits>
 
-// This header is used in the disassembler with libbase's logging. Only include ART logging
-// when no other logging macros are available. b/15436106, b/31338270
-#ifndef CHECK
-#include "base/logging.h"
-#endif
-
 #include "base/iteration_range.h"
+#include "base/logging.h"
 #include "base/stl_util.h"
 
 namespace art {
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index e00e62d..17873b5 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -28,6 +28,7 @@
 // Headers for LogMessage::LogLine.
 #ifdef ART_TARGET_ANDROID
 #include "cutils/log.h"
+#include <android/set_abort_message.h>
 #else
 #include <sys/types.h>
 #include <unistd.h>
@@ -39,24 +40,10 @@
 
 unsigned int gAborting = 0;
 
-static LogSeverity gMinimumLogSeverity = INFO;
 static std::unique_ptr<std::string> gCmdLine;
 static std::unique_ptr<std::string> gProgramInvocationName;
 static std::unique_ptr<std::string> gProgramInvocationShortName;
 
-// Print INTERNAL_FATAL messages directly instead of at destruction time. This only works on the
-// host right now: for the device, a stream buf collating output into lines and calling LogLine or
-// lower-level logging is necessary.
-#ifdef ART_TARGET_ANDROID
-static constexpr bool kPrintInternalFatalDirectly = false;
-#else
-static constexpr bool kPrintInternalFatalDirectly = !kIsTargetBuild;
-#endif
-
-static bool PrintDirectly(LogSeverity severity) {
-  return kPrintInternalFatalDirectly && severity == INTERNAL_FATAL;
-}
-
 const char* GetCmdLine() {
   return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr;
 }
@@ -70,6 +57,16 @@
                                                         : "art";
 }
 
+NO_RETURN
+static void RuntimeAborter(const char* abort_message) {
+#ifdef __ANDROID__
+  android_set_abort_message(abort_message);
+#else
+  UNUSED(abort_message);
+#endif
+  Runtime::Abort();
+}
+
 void InitLogging(char* argv[]) {
   if (gCmdLine.get() != nullptr) {
     return;
@@ -94,150 +91,14 @@
     // TODO: fall back to /proc/self/cmdline when argv is null on Linux.
     gCmdLine.reset(new std::string("<unset>"));
   }
-  const char* tags = getenv("ANDROID_LOG_TAGS");
-  if (tags == nullptr) {
-    return;
-  }
 
-  std::vector<std::string> specs;
-  Split(tags, ' ', &specs);
-  for (size_t i = 0; i < specs.size(); ++i) {
-    // "tag-pattern:[vdiwefs]"
-    std::string spec(specs[i]);
-    if (spec.size() == 3 && StartsWith(spec, "*:")) {
-      switch (spec[2]) {
-        case 'v':
-          gMinimumLogSeverity = VERBOSE;
-          continue;
-        case 'd':
-          gMinimumLogSeverity = DEBUG;
-          continue;
-        case 'i':
-          gMinimumLogSeverity = INFO;
-          continue;
-        case 'w':
-          gMinimumLogSeverity = WARNING;
-          continue;
-        case 'e':
-          gMinimumLogSeverity = ERROR;
-          continue;
-        case 'f':
-          gMinimumLogSeverity = FATAL;
-          continue;
-        // liblog will even suppress FATAL if you say 's' for silent, but that's crazy!
-        case 's':
-          gMinimumLogSeverity = FATAL;
-          continue;
-      }
-    }
-    LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags << ")";
-  }
-}
-
-// This indirection greatly reduces the stack impact of having
-// lots of checks/logging in a function.
-class LogMessageData {
- public:
-  LogMessageData(const char* file, unsigned int line, LogSeverity severity, int error)
-      : file_(GetFilenameBase(file)),
-        line_number_(line),
-        severity_(severity),
-        error_(error) {}
-
-  const char * GetFile() const {
-    return file_;
-  }
-
-  unsigned int GetLineNumber() const {
-    return line_number_;
-  }
-
-  LogSeverity GetSeverity() const {
-    return severity_;
-  }
-
-  int GetError() const {
-    return error_;
-  }
-
-  std::ostream& GetBuffer() {
-    return buffer_;
-  }
-
-  std::string ToString() const {
-    return buffer_.str();
-  }
-
- private:
-  std::ostringstream buffer_;
-  const char* const file_;
-  const unsigned int line_number_;
-  const LogSeverity severity_;
-  const int error_;
-
-  static const char* GetFilenameBase(const char* file) {
-    const char* last_slash = strrchr(file, '/');
-    return (last_slash == nullptr) ? file : last_slash + 1;
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error)
-  : data_(new LogMessageData(file, line, severity, error)) {
-  if (PrintDirectly(severity)) {
-    static constexpr char kLogCharacters[] = { 'V', 'D', 'I', 'W', 'E', 'F', 'F' };
-    static_assert(arraysize(kLogCharacters) == static_cast<size_t>(INTERNAL_FATAL) + 1,
-                  "Wrong character array size");
-    stream() << ProgramInvocationShortName() << " " << kLogCharacters[static_cast<size_t>(severity)]
-             << " " << getpid() << " " << ::art::GetTid() << " " << file << ":" <<  line << "]";
-  }
-}
-LogMessage::~LogMessage() {
-  if (PrintDirectly(data_->GetSeverity())) {
-    // Add newline at the end to match the not printing directly behavior.
-    std::cerr << '\n';
-  } else {
-    if (data_->GetSeverity() < gMinimumLogSeverity) {
-      return;  // No need to format something we're not going to output.
-    }
-
-    // Finish constructing the message.
-    if (data_->GetError() != -1) {
-      data_->GetBuffer() << ": " << strerror(data_->GetError());
-    }
-    std::string msg(data_->ToString());
-
-    // Do the actual logging with the lock held.
-    {
-      MutexLock mu(Thread::Current(), *Locks::logging_lock_);
-      if (msg.find('\n') == std::string::npos) {
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
-      } else {
-        msg += '\n';
-        size_t i = 0;
-        while (i < msg.size()) {
-          size_t nl = msg.find('\n', i);
-          msg[nl] = '\0';
-          LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
-          i = nl + 1;
-        }
-      }
-    }
-  }
-
-  // Abort if necessary.
-  if (data_->GetSeverity() == FATAL) {
-    Runtime::Abort();
-  }
-}
-
-std::ostream& LogMessage::stream() {
-  if (PrintDirectly(data_->GetSeverity())) {
-    return std::cerr;
-  }
-  return data_->GetBuffer();
+#ifdef __ANDROID__
+#define INIT_LOGGING_DEFAULT_LOGGER android::base::LogdLogger()
+#else
+#define INIT_LOGGING_DEFAULT_LOGGER android::base::StderrLogger
+#endif
+  android::base::InitLogging(argv, INIT_LOGGING_DEFAULT_LOGGER, RuntimeAborter);
+#undef INIT_LOGGING_DEFAULT_LOGGER
 }
 
 #ifdef ART_TARGET_ANDROID
@@ -245,31 +106,14 @@
   ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
   ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL
 };
-static_assert(arraysize(kLogSeverityToAndroidLogPriority) == INTERNAL_FATAL + 1,
+static_assert(arraysize(kLogSeverityToAndroidLogPriority) == ::android::base::FATAL + 1,
               "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity");
 #endif
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_severity,
-                         const char* message) {
-#ifdef ART_TARGET_ANDROID
-  const char* tag = ProgramInvocationShortName();
-  int priority = kLogSeverityToAndroidLogPriority[static_cast<size_t>(log_severity)];
-  if (priority == ANDROID_LOG_FATAL) {
-    LOG_PRI(priority, tag, "%s:%u] %s", file, line, message);
-  } else {
-    LOG_PRI(priority, tag, "%s", message);
-  }
-#else
-  static const char* log_characters = "VDIWEFF";
-  CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
-  char severity = log_characters[log_severity];
-  fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n",
-          ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(), file, line, message);
-#endif
-}
-
-void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
-                                 const char* message) {
+void LogHelper::LogLineLowStack(const char* file,
+                                unsigned int line,
+                                LogSeverity log_severity,
+                                const char* message) {
 #ifdef ART_TARGET_ANDROID
   // Use android_writeLog() to avoid stack-based buffers used by android_printLog().
   const char* tag = ProgramInvocationShortName();
@@ -292,8 +136,9 @@
   }
 #else
   static constexpr char kLogCharacters[] = { 'V', 'D', 'I', 'W', 'E', 'F', 'F' };
-  static_assert(arraysize(kLogCharacters) == static_cast<size_t>(INTERNAL_FATAL) + 1,
-                "Wrong character array size");
+  static_assert(
+      arraysize(kLogCharacters) == static_cast<size_t>(::android::base::FATAL) + 1,
+      "Wrong character array size");
 
   const char* program_name = ProgramInvocationShortName();
   TEMP_FAILURE_RETRY(write(STDERR_FILENO, program_name, strlen(program_name)));
@@ -310,13 +155,4 @@
 #endif  // ART_TARGET_ANDROID
 }
 
-ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
-  old_ = gMinimumLogSeverity;
-  gMinimumLogSeverity = level;
-}
-
-ScopedLogSeverity::~ScopedLogSeverity() {
-  gMinimumLogSeverity = old_;
-}
-
 }  // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 185aa0e..5f84204 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -18,20 +18,16 @@
 #define ART_RUNTIME_BASE_LOGGING_H_
 
 #include <ostream>
+#include <sstream>
 
+#include "android-base/logging.h"
 #include "base/macros.h"
 
 namespace art {
 
-enum LogSeverity {
-  VERBOSE,
-  DEBUG,
-  INFO,
-  WARNING,
-  ERROR,
-  FATAL,
-  INTERNAL_FATAL,  // For Runtime::Abort.
-};
+// Make libbase's LogSeverity more easily available.
+using ::android::base::LogSeverity;
+using ::android::base::ScopedLogSeverity;
 
 // The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code,
 // and the "-verbose:" command line argument.
@@ -89,172 +85,28 @@
 // hasn't been performed then just returns "art"
 extern const char* ProgramInvocationShortName();
 
-// Logs a message to logcat on Android otherwise to stderr. If the severity is FATAL it also causes
-// an abort. For example: LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream()
+class LogHelper {
+ public:
+  // A logging helper for logging a single line. Can be used with little stack.
+  static void LogLineLowStack(const char* file,
+                              unsigned int line,
+                              android::base::LogSeverity severity,
+                              const char* msg);
 
-// A variant of LOG that also logs the current errno value. To be used when library calls fail.
-#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream()
-
-// Marker that code is yet to be implemented.
-#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+ private:
+  DISALLOW_ALLOCATION();
+  DISALLOW_COPY_AND_ASSIGN(LogHelper);
+};
 
 // Is verbose logging enabled for the given module? Where the module is defined in LogVerbosity.
 #define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module)
 
 // Variant of LOG that logs when verbose logging is enabled for a module. For example,
 // VLOG(jni) << "A JNI operation was performed";
-#define VLOG(module) \
-  if (VLOG_IS_ON(module)) \
-    ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
+#define VLOG(module) if (VLOG_IS_ON(module)) LOG(INFO)
 
 // Return the stream associated with logging for the given module.
-#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream()
-
-// Check whether condition x holds and LOG(FATAL) if not. The value of the expression x is only
-// evaluated once. Extra logging can be appended using << after. For example,
-// CHECK(false == true) results in a log message of "Check failed: false == true".
-#define CHECK(x) \
-  if (UNLIKELY(!(x))) \
-    ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \
-        << "Check failed: " #x << " "
-
-// Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP) \
-  for (auto _values = ::art::MakeEagerEvaluator(LHS, RHS); \
-       UNLIKELY(!(_values.lhs OP _values.rhs)); /* empty */) \
-    ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \
-        << "Check failed: " << #LHS << " " << #OP << " " << #RHS \
-        << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
-
-
-// Check whether a condition holds between x and y, LOG(FATAL) if not. The value of the expressions
-// x and y is evaluated once. Extra logging can be appended using << after. For example,
-// CHECK_NE(0 == 1, false) results in "Check failed: false != false (0==1=false, false=false) ".
-#define CHECK_EQ(x, y) CHECK_OP(x, y, ==)
-#define CHECK_NE(x, y) CHECK_OP(x, y, !=)
-#define CHECK_LE(x, y) CHECK_OP(x, y, <=)
-#define CHECK_LT(x, y) CHECK_OP(x, y, <)
-#define CHECK_GE(x, y) CHECK_OP(x, y, >=)
-#define CHECK_GT(x, y) CHECK_OP(x, y, >)
-
-// Helper for CHECK_STRxx(s1,s2) macros.
-#define CHECK_STROP(s1, s2, sense) \
-  if (UNLIKELY((strcmp(s1, s2) == 0) != (sense))) \
-    LOG(::art::FATAL) << "Check failed: " \
-        << "\"" << (s1) << "\"" \
-        << ((sense) ? " == " : " != ") \
-        << "\"" << (s2) << "\""
-
-// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
-#define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
-#define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false)
-
-// Perform the pthread function call(args), LOG(FATAL) on error.
-#define CHECK_PTHREAD_CALL(call, args, what) \
-  do { \
-    int rc = call args; \
-    if (rc != 0) { \
-      errno = rc; \
-      PLOG(::art::FATAL) << # call << " failed for " << (what); \
-    } \
-  } while (false)
-
-
-// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally CHECK should be
-// used unless profiling identifies a CHECK as being in performance critical code.
-#if defined(NDEBUG)
-static constexpr bool kEnableDChecks = false;
-#else
-static constexpr bool kEnableDChecks = true;
-#endif
-
-#define DCHECK(x) if (::art::kEnableDChecks) CHECK(x)
-#define DCHECK_EQ(x, y) if (::art::kEnableDChecks) CHECK_EQ(x, y)
-#define DCHECK_NE(x, y) if (::art::kEnableDChecks) CHECK_NE(x, y)
-#define DCHECK_LE(x, y) if (::art::kEnableDChecks) CHECK_LE(x, y)
-#define DCHECK_LT(x, y) if (::art::kEnableDChecks) CHECK_LT(x, y)
-#define DCHECK_GE(x, y) if (::art::kEnableDChecks) CHECK_GE(x, y)
-#define DCHECK_GT(x, y) if (::art::kEnableDChecks) CHECK_GT(x, y)
-#define DCHECK_STREQ(s1, s2) if (::art::kEnableDChecks) CHECK_STREQ(s1, s2)
-#define DCHECK_STRNE(s1, s2) if (::art::kEnableDChecks) CHECK_STRNE(s1, s2)
-
-// Temporary class created to evaluate the LHS and RHS, used with MakeEagerEvaluator to infer the
-// types of LHS and RHS.
-template <typename LHS, typename RHS>
-struct EagerEvaluator {
-  constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { }
-  LHS lhs;
-  RHS rhs;
-};
-
-// Helper function for CHECK_xx.
-template <typename LHS, typename RHS>
-constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
-  return EagerEvaluator<LHS, RHS>(lhs, rhs);
-}
-
-// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated as strings. To
-// compare strings use CHECK_STREQ and CHECK_STRNE. We rely on signed/unsigned warnings to
-// protect you against combinations not explicitly listed below.
-#define EAGER_PTR_EVALUATOR(T1, T2) \
-  template <> struct EagerEvaluator<T1, T2> { \
-    EagerEvaluator(T1 l, T2 r) \
-        : lhs(reinterpret_cast<const void*>(l)), \
-          rhs(reinterpret_cast<const void*>(r)) { } \
-    const void* lhs; \
-    const void* rhs; \
-  }
-EAGER_PTR_EVALUATOR(const char*, const char*);
-EAGER_PTR_EVALUATOR(const char*, char*);
-EAGER_PTR_EVALUATOR(char*, const char*);
-EAGER_PTR_EVALUATOR(char*, char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(const unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, const unsigned char*);
-EAGER_PTR_EVALUATOR(unsigned char*, unsigned char*);
-EAGER_PTR_EVALUATOR(const signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(const signed char*, signed char*);
-EAGER_PTR_EVALUATOR(signed char*, const signed char*);
-EAGER_PTR_EVALUATOR(signed char*, signed char*);
-
-// Data for the log message, not stored in LogMessage to avoid increasing the stack size.
-class LogMessageData;
-
-// A LogMessage is a temporarily scoped object used by LOG and the unlikely part of a CHECK. The
-// destructor will abort if the severity is FATAL.
-class LogMessage {
- public:
-  LogMessage(const char* file, unsigned int line, LogSeverity severity, int error);
-
-  ~LogMessage();  // TODO: enable REQUIRES(!Locks::logging_lock_).
-
-  // Returns the stream associated with the message, the LogMessage performs output when it goes
-  // out of scope.
-  std::ostream& stream();
-
-  // The routine that performs the actual logging.
-  static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* msg);
-
-  // A variant of the above for use with little stack.
-  static void LogLineLowStack(const char* file, unsigned int line, LogSeverity severity,
-                              const char* msg);
-
- private:
-  const std::unique_ptr<LogMessageData> data_;
-
-  DISALLOW_COPY_AND_ASSIGN(LogMessage);
-};
-
-// Allows to temporarily change the minimum severity level for logging.
-class ScopedLogSeverity {
- public:
-  explicit ScopedLogSeverity(LogSeverity level);
-  ~ScopedLogSeverity();
-
- private:
-  LogSeverity old_;
-};
+#define VLOG_STREAM(module) LOG_STREAM(INFO)
 
 }  // namespace art
 
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 43c38c4..1183dea 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -26,7 +26,7 @@
 #include "base/value_object.h"
 #include "mutex-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -328,14 +328,20 @@
   bool shutting_down = IsShuttingDown();
 #if ART_USE_FUTEXES
   if (state_.LoadRelaxed() != 0) {
-    LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_;
+    LOG(shutting_down
+            ? ::android::base::WARNING
+            : ::android::base::FATAL) << "destroying mutex with owner: " << exclusive_owner_;
   } else {
     if (exclusive_owner_ != 0) {
-      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found an owner on unlocked mutex "
+      LOG(shutting_down
+              ? ::android::base::WARNING
+              : ::android::base::FATAL) << "unexpectedly found an owner on unlocked mutex "
                                            << name_;
     }
     if (num_contenders_.LoadSequentiallyConsistent() != 0) {
-      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found a contender on mutex " << name_;
+      LOG(shutting_down
+              ? ::android::base::WARNING
+              : ::android::base::FATAL) << "unexpectedly found a contender on mutex " << name_;
     }
   }
 #else
@@ -346,7 +352,9 @@
     errno = rc;
     // TODO: should we just not log at all if shutting down? this could be the logging mutex!
     MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
-    PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_;
+    PLOG(shutting_down
+             ? ::android::base::WARNING
+             : ::android::base::FATAL) << "pthread_mutex_destroy failed for " << name_;
   }
 #endif
 }
@@ -480,9 +488,11 @@
         if (this != Locks::logging_lock_) {
           LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_;
         } else {
-          LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL,
-                              StringPrintf("Unexpected state_ %d in unlock for %s",
-                                           cur_state, name_).c_str());
+          LogHelper::LogLineLowStack(__FILE__,
+                                     __LINE__,
+                                     ::android::base::FATAL_WITHOUT_ABORT,
+                                     StringPrintf("Unexpected state_ %d in unlock for %s",
+                                                  cur_state, name_).c_str());
           _exit(1);
         }
       }
@@ -762,7 +772,10 @@
   if (num_waiters_!= 0) {
     Runtime* runtime = Runtime::Current();
     bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current());
-    LOG(shutting_down ? WARNING : FATAL) << "ConditionVariable::~ConditionVariable for " << name_
+    LOG(shutting_down
+           ? ::android::base::WARNING
+           : ::android::base::FATAL)
+        << "ConditionVariable::~ConditionVariable for " << name_
         << " called with " << num_waiters_ << " waiters.";
   }
 #else
@@ -774,7 +787,9 @@
     MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
     Runtime* runtime = Runtime::Current();
     bool shutting_down = (runtime == nullptr) || runtime->IsShuttingDownLocked();
-    PLOG(shutting_down ? WARNING : FATAL) << "pthread_cond_destroy failed for " << name_;
+    PLOG(shutting_down
+             ? ::android::base::WARNING
+             : ::android::base::FATAL) << "pthread_cond_destroy failed for " << name_;
   }
 #endif
 }
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 8af9fa5..91e31d8 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -78,7 +78,6 @@
   kAllocSpaceLock,
   kBumpPointerSpaceBlockLock,
   kArenaPoolLock,
-  kDexFileMethodInlinerLock,
   kDexFileToMethodInlinerMapLock,
   kInternTableLock,
   kOatFileSecondaryLookupLock,
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index a4cf249..a53dcea 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -20,11 +20,7 @@
 #include <algorithm>
 #include <sstream>
 
-// This header is used in the disassembler with libbase's logging. Only include ART logging
-// when no other logging macros are available. b/15436106, b/31338270
-#ifndef CHECK
 #include "base/logging.h"
-#endif
 
 namespace art {
 
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 48e3ceb..4498198 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -64,16 +64,16 @@
 void FdFile::Destroy() {
   if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
     if (guard_state_ < GuardState::kFlushed) {
-      LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
+      LOG(ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
     }
     if (guard_state_ < GuardState::kClosed) {
-      LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
+      LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
     }
     CHECK_GE(guard_state_, GuardState::kClosed);
   }
   if (auto_close_ && fd_ != -1) {
     if (Close() != 0) {
-      PLOG(::art::WARNING) << "Failed to close file " << file_path_;
+      PLOG(WARNING) << "Failed to close file " << file_path_;
     }
   }
 }
@@ -104,7 +104,7 @@
   if (kCheckSafeUsage) {
     if (guard_state_ < GuardState::kNoCheck) {
       if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
-        LOG(::art::ERROR) << warning;
+        LOG(ERROR) << warning;
       }
       guard_state_ = target;
     }
@@ -117,7 +117,7 @@
       if (guard_state_ < target) {
         guard_state_ = target;
       } else if (target < guard_state_) {
-        LOG(::art::ERROR) << warning;
+        LOG(ERROR) << warning;
       }
     }
   }
@@ -350,13 +350,13 @@
   DCHECK(!read_only_mode_);
   int flush_result = TEMP_FAILURE_RETRY(Flush());
   if (flush_result != 0) {
-    LOG(::art::ERROR) << "CloseOrErase failed while flushing a file.";
+    LOG(ERROR) << "CloseOrErase failed while flushing a file.";
     Erase();
     return flush_result;
   }
   int close_result = TEMP_FAILURE_RETRY(Close());
   if (close_result != 0) {
-    LOG(::art::ERROR) << "CloseOrErase failed while closing a file.";
+    LOG(ERROR) << "CloseOrErase failed while closing a file.";
     Erase();
     return close_result;
   }
@@ -367,11 +367,11 @@
   DCHECK(!read_only_mode_);
   int flush_result = TEMP_FAILURE_RETRY(Flush());
   if (flush_result != 0) {
-    LOG(::art::ERROR) << "FlushClose failed while flushing a file.";
+    LOG(ERROR) << "FlushClose failed while flushing a file.";
   }
   int close_result = TEMP_FAILURE_RETRY(Close());
   if (close_result != 0) {
-    LOG(::art::ERROR) << "FlushClose failed while closing a file.";
+    LOG(ERROR) << "FlushClose failed while closing a file.";
   }
   return (flush_result != 0) ? flush_result : close_result;
 }
@@ -383,7 +383,7 @@
 bool FdFile::ClearContent() {
   DCHECK(!read_only_mode_);
   if (SetLength(0) < 0) {
-    PLOG(art::ERROR) << "Failed to reset the length";
+    PLOG(ERROR) << "Failed to reset the length";
     return false;
   }
   return ResetOffset();
@@ -393,7 +393,7 @@
   DCHECK(!read_only_mode_);
   off_t rc =  TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
   if (rc == static_cast<off_t>(-1)) {
-    PLOG(art::ERROR) << "Failed to reset the offset";
+    PLOG(ERROR) << "Failed to reset the offset";
     return false;
   }
   return true;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 6683f13..9f07702 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -36,7 +36,7 @@
 #include "mirror/string-inl.h"
 #include "mirror/throwable.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "well_known_classes.h"
 
@@ -269,13 +269,13 @@
    */
   bool CheckInstanceFieldID(ScopedObjectAccess& soa, jobject java_object, jfieldID fid)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     if (o == nullptr) {
       AbortF("field operation on NULL object: %p", java_object);
       return false;
     }
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o.Decode())) {
+      Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("field operation on invalid %s: %p",
              ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
              java_object);
@@ -333,15 +333,15 @@
       return false;
     }
     if (invoke != kVirtual) {
-      mirror::Class* c = soa.Decode<mirror::Class*>(jc);
-      if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+      ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
+      if (!m->GetDeclaringClass()->IsAssignableFrom(c.Decode())) {
         AbortF("can't call %s %s with class %s", invoke == kStatic ? "static" : "nonvirtual",
             PrettyMethod(m).c_str(), PrettyClass(c).c_str());
         return false;
       }
     }
     if (invoke != kStatic) {
-      mirror::Object* o = soa.Decode<mirror::Object*>(jobj);
+      ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(jobj);
       if (o == nullptr) {
         AbortF("can't call %s on null object", PrettyMethod(m).c_str());
         return false;
@@ -360,12 +360,12 @@
    */
   bool CheckStaticFieldID(ScopedObjectAccess& soa, jclass java_class, jfieldID fid)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
     ArtField* f = CheckFieldID(soa, fid);
     if (f == nullptr) {
       return false;
     }
-    if (f->GetDeclaringClass() != c) {
+    if (c != f->GetDeclaringClass()) {
       AbortF("static jfieldID %p not valid for class %s", fid, PrettyClass(c).c_str());
       return false;
     }
@@ -387,8 +387,8 @@
     if (m == nullptr) {
       return false;
     }
-    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
-    if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
+    if (!m->GetDeclaringClass()->IsAssignableFrom(c.Decode())) {
       AbortF("can't call static %s on class %s", PrettyMethod(m).c_str(), PrettyClass(c).c_str());
       return false;
     }
@@ -408,7 +408,7 @@
     if (m == nullptr) {
       return false;
     }
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     if (o == nullptr) {
       AbortF("can't call %s on null object", PrettyMethod(m).c_str());
       return false;
@@ -557,14 +557,14 @@
 
   bool CheckReflectedMethod(ScopedObjectAccess& soa, jobject jmethod)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Object* method = soa.Decode<mirror::Object*>(jmethod);
+    ObjPtr<mirror::Object> method = soa.Decode<mirror::Object>(jmethod);
     if (method == nullptr) {
       AbortF("expected non-null method");
       return false;
     }
     mirror::Class* c = method->GetClass();
-    if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Method) != c &&
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Constructor) != c) {
+    if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Method) != c &&
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Constructor) != c) {
       AbortF("expected java.lang.reflect.Method or "
           "java.lang.reflect.Constructor but got object of type %s: %p",
           PrettyTypeOf(method).c_str(), jmethod);
@@ -589,13 +589,13 @@
 
   bool CheckReflectedField(ScopedObjectAccess& soa, jobject jfield)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Object* field = soa.Decode<mirror::Object*>(jfield);
+    ObjPtr<mirror::Object> field = soa.Decode<mirror::Object>(jfield);
     if (field == nullptr) {
       AbortF("expected non-null java.lang.reflect.Field");
       return false;
     }
     mirror::Class* c = field->GetClass();
-    if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Field) != c) {
+    if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Field) != c) {
       AbortF("expected java.lang.reflect.Field but got object of type %s: %p",
              PrettyTypeOf(field).c_str(), jfield);
       return false;
@@ -605,10 +605,10 @@
 
   bool CheckThrowable(ScopedObjectAccess& soa, jthrowable jobj)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
     if (!obj->GetClass()->IsThrowableClass()) {
       AbortF("expected java.lang.Throwable but got object of type "
-             "%s: %p", PrettyTypeOf(obj).c_str(), obj);
+             "%s: %p", PrettyTypeOf(obj).c_str(), obj.Decode());
       return false;
     }
     return true;
@@ -616,10 +616,10 @@
 
   bool CheckThrowableClass(ScopedObjectAccess& soa, jclass jc)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
     if (!c->IsThrowableClass()) {
       AbortF("expected java.lang.Throwable class but got object of "
-             "type %s: %p", PrettyDescriptor(c).c_str(), c);
+             "type %s: %p", PrettyDescriptor(c).c_str(), c.Decode());
       return false;
     }
     return true;
@@ -647,9 +647,9 @@
 
   bool CheckInstantiableNonArray(ScopedObjectAccess& soa, jclass jc)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
     if (!c->IsInstantiableNonArray()) {
-      AbortF("can't make objects of type %s: %p", PrettyDescriptor(c).c_str(), c);
+      AbortF("can't make objects of type %s: %p", PrettyDescriptor(c).c_str(), c.Decode());
       return false;
     }
     return true;
@@ -660,7 +660,7 @@
     if (!CheckArray(soa, array)) {
       return false;
     }
-    mirror::Array* a = soa.Decode<mirror::Array*>(array);
+    ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(array);
     if (a->GetClass()->GetComponentType()->GetPrimitiveType() != type) {
       AbortF("incompatible array type %s expected %s[]: %p",
              PrettyDescriptor(a->GetClass()).c_str(), PrettyDescriptor(type).c_str(), array);
@@ -692,20 +692,20 @@
       return false;
     }
     if (is_static) {
-      mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+      ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
       if (o == nullptr || !o->IsClass()) {
         AbortF("attempt to access static field %s with a class argument of type %s: %p",
                PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
         return false;
       }
-      mirror::Class* c = o->AsClass();
-      if (field->GetDeclaringClass() != c) {
+      ObjPtr<mirror::Class> c = o->AsClass();
+      if (c != field->GetDeclaringClass()) {
         AbortF("attempt to access static field %s with an incompatible class argument of %s: %p",
                PrettyField(field).c_str(), PrettyDescriptor(c).c_str(), fid);
         return false;
       }
     } else {
-      mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+      ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
       if (o == nullptr || !field->GetDeclaringClass()->IsAssignableFrom(o->GetClass())) {
         AbortF("attempt to access field %s from an object argument of type %s: %p",
                PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
@@ -763,7 +763,7 @@
       }
     }
 
-    mirror::Object* obj = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(java_object);
     if (obj == nullptr) {
       // Either java_object is invalid or is a cleared weak.
       IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -772,21 +772,21 @@
         okay = false;
       } else {
         obj = soa.Vm()->DecodeWeakGlobal(soa.Self(), ref);
-        okay = Runtime::Current()->IsClearedJniWeakGlobal(obj);
+        okay = Runtime::Current()->IsClearedJniWeakGlobal(obj.Decode());
       }
       if (!okay) {
         AbortF("%s is an invalid %s: %p (%p)",
                what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
-               java_object, obj);
+               java_object, obj.Decode());
         return false;
       }
     }
 
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+      Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("%s is an invalid %s: %p (%p)",
              what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
-             java_object, obj);
+             java_object, obj.Decode());
       return false;
     }
 
@@ -936,10 +936,10 @@
         break;
       case 'c': {  // jclass
         jclass jc = arg.c;
-        mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+        ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
         if (c == nullptr) {
           *msg += "NULL";
-        } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+        } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c.Decode())) {
           StringAppendF(msg, "INVALID POINTER:%p", jc);
         } else if (!c->IsClass()) {
           *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
@@ -1107,12 +1107,12 @@
       return false;
     }
 
-    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-    if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+    ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(java_array);
+    if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a.Decode()))) {
+      Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("jarray is an invalid %s: %p (%p)",
              ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
-             java_array, a);
+             java_array, a.Decode());
       return false;
     } else if (!a->IsArrayInstance()) {
       AbortF("jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
@@ -1146,7 +1146,7 @@
     ArtField* f = soa.DecodeField(fid);
     // TODO: Better check here.
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass())) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+      Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("invalid jfieldID: %p", fid);
       return nullptr;
     }
@@ -1162,7 +1162,7 @@
     ArtMethod* m = soa.DecodeMethod(mid);
     // TODO: Better check here.
     if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m->GetDeclaringClass())) {
-      Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+      Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("invalid jmethodID: %p", mid);
       return nullptr;
     }
@@ -1411,7 +1411,7 @@
                                    void* original_ptr) {
     ScopedObjectAccess soa(env);
 
-    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+    ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(java_array);
     size_t component_size = a->GetClass()->GetComponentSize();
     size_t byte_count = a->GetLength() * component_size;
     void* result = Create(original_ptr, byte_count, true);
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 843d4c1..ab712f9 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -19,7 +19,7 @@
 
 #include "art_method-inl.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack_map.h"
 
 namespace art {
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index caabcde..51e5aae 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -67,9 +67,10 @@
   // MethodVerifier refuses methods with string_idx out of bounds.
   DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds());;
   mirror::String* string =
-        mirror::StringDexCachePair::LookupString(declaring_class->GetDexCacheStrings(),
-                                                 string_idx,
-                                                 mirror::DexCache::kDexCacheStringCacheSize).Read();
+        mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+                                           string_idx,
+                                           mirror::DexCache::kDexCacheStringCacheSize).Read();
+  Thread::PoisonObjectPointersIfDebug();
   if (UNLIKELY(string == nullptr)) {
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -84,6 +85,7 @@
 
 inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) {
   mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+  Thread::PoisonObjectPointersIfDebug();
   if (UNLIKELY(resolved_type == nullptr)) {
     mirror::Class* declaring_class = referrer->GetDeclaringClass();
     StackHandleScope<2> hs(Thread::Current());
@@ -101,6 +103,7 @@
   mirror::Class* declaring_class = referrer->GetDeclaringClass();
   mirror::DexCache* dex_cache_ptr = declaring_class->GetDexCache();
   mirror::Class* resolved_type = dex_cache_ptr->GetResolvedType(type_idx);
+  Thread::PoisonObjectPointersIfDebug();
   if (UNLIKELY(resolved_type == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr));
@@ -148,6 +151,7 @@
                                              ArtMethod* referrer,
                                              InvokeType type) {
   ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);
+  Thread::PoisonObjectPointersIfDebug();
   if (UNLIKELY(resolved_method == nullptr)) {
     mirror::Class* declaring_class = referrer->GetDeclaringClass();
     StackHandleScope<2> hs(self);
@@ -179,6 +183,7 @@
                                            bool is_static) {
   mirror::Class* declaring_class = referrer->GetDeclaringClass();
   ArtField* resolved_field = GetResolvedField(field_idx, declaring_class);
+  Thread::PoisonObjectPointersIfDebug();
   if (UNLIKELY(resolved_field == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 845e39a..7dea614 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -56,6 +56,8 @@
 #include "gc/space/image_space.h"
 #include "handle_scope-inl.h"
 #include "image-inl.h"
+#include "imt_conflict_table.h"
+#include "imtable-inl.h"
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
@@ -71,6 +73,8 @@
 #include "mirror/field.h"
 #include "mirror/iftable-inl.h"
 #include "mirror/method.h"
+#include "mirror/method_type.h"
+#include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -87,7 +91,7 @@
 #include "os.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "trace.h"
 #include "utils.h"
@@ -634,6 +638,18 @@
   SetClassRoot(kJavaLangReflectMethodArrayClass, class_root);
   mirror::Method::SetArrayClass(class_root);
 
+  // Create java.lang.invoke.MethodType.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeMethodType, class_root);
+  mirror::MethodType::SetClass(class_root);
+
+  // Create java.lang.invoke.MethodHandleImpl.class root
+  class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandleImpl;");
+  CHECK(class_root != nullptr);
+  SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
+  mirror::MethodHandleImpl::SetClass(class_root);
+
   // java.lang.ref classes need to be specially flagged, but otherwise are normal classes
   // finish initializing Reference class
   mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
@@ -1030,6 +1046,8 @@
   mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass));
   mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
   mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
+  mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
+  mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
   mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
   mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
   mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -1067,8 +1085,8 @@
 bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                     mirror::ClassLoader* class_loader) {
   return class_loader == nullptr ||
-      class_loader->GetClass() ==
-          soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader);
+       soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader) ==
+           class_loader->GetClass();
 }
 
 static mirror::String* GetDexPathListElementName(ScopedObjectAccessUnchecked& soa,
@@ -1107,8 +1125,8 @@
   CHECK(dex_path_list_field != nullptr);
   CHECK(dex_elements_field != nullptr);
   while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
-    if (class_loader->GetClass() !=
-        soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
+    if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
+        class_loader->GetClass()) {
       *error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str());
       // Unsupported class loader.
       return false;
@@ -1269,7 +1287,7 @@
       const DexFile* const dex_file = dex_cache->GetDexFile();
       const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
       if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-      // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
+        // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
         // copy over the arrays.
         DCHECK(dex_file != nullptr);
         size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
@@ -1279,10 +1297,19 @@
         const size_t num_types = dex_file->NumTypeIds();
         const size_t num_methods = dex_file->NumMethodIds();
         const size_t num_fields = dex_file->NumFieldIds();
+        size_t num_method_types = 0;
+        if (Runtime::Current()->IsMethodHandlesEnabled()) {
+          num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+          if (dex_file->NumProtoIds() < num_method_types) {
+            num_method_types = dex_file->NumProtoIds();
+          }
+        }
+
         CHECK_EQ(num_strings, dex_cache->NumStrings());
         CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
         CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
         CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
+        CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
         DexCacheArraysLayout layout(image_pointer_size_, dex_file);
         uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
         if (num_strings != 0u) {
@@ -1290,8 +1317,8 @@
           mirror::StringDexCacheType* const strings =
               reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
           for (size_t j = 0; j < num_strings; ++j) {
-            DCHECK_EQ(strings[j].load(std::memory_order_relaxed).string_index, 0u);
-            DCHECK(strings[j].load(std::memory_order_relaxed).string_pointer.IsNull());
+            DCHECK_EQ(strings[j].load(std::memory_order_relaxed).index, 0u);
+            DCHECK(strings[j].load(std::memory_order_relaxed).object.IsNull());
             strings[j].store(image_resolved_strings[j].load(std::memory_order_relaxed),
                              std::memory_order_relaxed);
           }
@@ -1333,6 +1360,25 @@
           std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
           dex_cache->SetResolvedFields(fields);
         }
+        if (num_method_types != 0u) {
+          // NOTE: We currently (Sep 2016) do not resolve any method types at
+          // compile time, but plan to in the future. This code exists for the
+          // sake of completeness.
+          mirror::MethodTypeDexCacheType* const image_resolved_method_types =
+              dex_cache->GetResolvedMethodTypes();
+          mirror::MethodTypeDexCacheType* const method_types =
+              reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+                  raw_arrays + layout.MethodTypesOffset());
+          for (size_t j = 0; j < num_method_types; ++j) {
+            DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u);
+            DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull());
+            method_types[j].store(
+                image_resolved_method_types[j].load(std::memory_order_relaxed),
+                std::memory_order_relaxed);
+          }
+
+          dex_cache->SetResolvedMethodTypes(method_types);
+        }
       }
       {
         WriterMutexLock mu2(self, dex_lock_);
@@ -1685,7 +1731,7 @@
         return false;
       }
       // Add the temporary dex path list elements at the end.
-      auto* elements = soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements);
+      auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
       for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
         mirror::Object* element = elements->GetWithoutChecks(i);
         if (element != nullptr) {
@@ -2030,6 +2076,8 @@
   mirror::IntArray::ResetArrayClass();
   mirror::LongArray::ResetArrayClass();
   mirror::ShortArray::ResetArrayClass();
+  mirror::MethodType::ResetClass();
+  mirror::MethodHandleImpl::ResetClass();
   Thread* const self = Thread::Current();
   for (const ClassLoaderData& data : class_loaders_) {
     DeleteClassLoader(self, data);
@@ -2084,6 +2132,7 @@
     // Zero-initialized.
     raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
   }
+
   mirror::StringDexCacheType* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
       reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
   GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr :
@@ -2092,10 +2141,35 @@
       reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
   ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr :
       reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+
   size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
   if (dex_file.NumStringIds() < num_strings) {
     num_strings = dex_file.NumStringIds();
   }
+
+  // Note that we allocate the method type dex caches regardless of this flag,
+  // and we make sure here that they're not used by the runtime. This is in the
+  // interest of simplicity and to avoid extensive compiler and layout class changes.
+  //
+  // If this needs to be mitigated in a production system running this code,
+  // DexCache::kDexCacheMethodTypeCacheSize can be set to zero.
+  const bool is_method_handles_enabled = Runtime::Current()->IsMethodHandlesEnabled();
+  mirror::MethodTypeDexCacheType* method_types = nullptr;
+  size_t num_method_types = 0;
+
+  if (is_method_handles_enabled) {
+    if (dex_file.NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) {
+      num_method_types = dex_file.NumProtoIds();
+    } else {
+      num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+    }
+
+    if (num_method_types > 0) {
+      method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+          raw_arrays + layout.MethodTypesOffset());
+    }
+  }
+
   DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
                  "Expected raw_arrays to align to StringDexCacheType.";
   DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -2107,8 +2181,8 @@
   if (kIsDebugBuild) {
     // Sanity check to make sure all the dex cache arrays are empty. b/28992179
     for (size_t i = 0; i < num_strings; ++i) {
-      CHECK_EQ(strings[i].load(std::memory_order_relaxed).string_index, 0u);
-      CHECK(strings[i].load(std::memory_order_relaxed).string_pointer.IsNull());
+      CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u);
+      CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
     }
     for (size_t i = 0; i < dex_file.NumTypeIds(); ++i) {
       CHECK(types[i].Read<kWithoutReadBarrier>() == nullptr);
@@ -2119,10 +2193,17 @@
     for (size_t i = 0; i < dex_file.NumFieldIds(); ++i) {
       CHECK(mirror::DexCache::GetElementPtrSize(fields, i, image_pointer_size_) == nullptr);
     }
+    for (size_t i = 0; i < num_method_types; ++i) {
+      CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
+      CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
+    }
   }
   if (strings != nullptr) {
     mirror::StringDexCachePair::Initialize(strings);
   }
+  if (method_types != nullptr) {
+    mirror::MethodTypeDexCachePair::Initialize(method_types);
+  }
   dex_cache->Init(&dex_file,
                   location.Get(),
                   strings,
@@ -2133,6 +2214,8 @@
                   dex_file.NumMethodIds(),
                   fields,
                   dex_file.NumFieldIds(),
+                  method_types,
+                  num_method_types,
                   image_pointer_size_);
   return dex_cache.Get();
 }
@@ -2167,6 +2250,7 @@
                                            const char* descriptor,
                                            mirror::Class* klass) {
   DCHECK(klass != nullptr);
+  Thread::PoisonObjectPointersIfDebug();
 
   // For temporary classes we must wait for them to be retired.
   if (init_done_ && klass->IsTemp()) {
@@ -2283,8 +2367,8 @@
   }
 
   // Unsupported class-loader?
-  if (class_loader->GetClass() !=
-      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
+  if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
+      class_loader->GetClass()) {
     *result = nullptr;
     return false;
   }
@@ -2378,6 +2462,7 @@
   DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
   DCHECK(self != nullptr);
   self->AssertNoPendingException();
+  self->PoisonObjectPointers();  // For DefineClass, CreateArrayClass, etc...
   if (descriptor[1] == '\0') {
     // only the descriptors of primitive types should be 1 character long, also avoid class lookup
     // for primitive classes that aren't backed by dex files.
@@ -2460,7 +2545,7 @@
       return nullptr;
     } else {
       // success, return mirror::Class*
-      return soa.Decode<mirror::Class*>(result.get());
+      return soa.Decode<mirror::Class>(result.get()).Decode();
     }
   }
   UNREACHABLE();
@@ -4183,9 +4268,9 @@
   // Set the class access flags incl. VerificationAttempted, so we do not try to set the flag on
   // the methods.
   klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal | kAccVerificationAttempted);
-  klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader));
+  klass->SetClassLoader(soa.Decode<mirror::ClassLoader>(loader).Decode());
   DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
-  klass->SetName(soa.Decode<mirror::String*>(name));
+  klass->SetName(soa.Decode<mirror::String>(name).Decode());
   klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
   mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
   std::string descriptor(GetDescriptorForProxy(klass.Get()));
@@ -4223,7 +4308,7 @@
   const size_t num_direct_methods = 1;
 
   // They have as many virtual methods as the array
-  auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>*>(methods));
+  auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>>(methods));
   DCHECK_EQ(h_methods->GetClass(), mirror::Method::ArrayClass())
       << PrettyClass(h_methods->GetClass());
   const size_t num_virtual_methods = h_methods->GetLength();
@@ -4265,7 +4350,7 @@
     // Link the fields and virtual methods, creating vtable and iftables.
     // The new class will replace the old one in the class table.
     Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
-        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)));
+        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
     if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
       mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
       return nullptr;
@@ -4276,11 +4361,13 @@
   klass.Assign(new_class.Get());
 
   CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
-  interfaces_sfield.SetObject<false>(klass.Get(),
-                                     soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+  interfaces_sfield.SetObject<false>(
+      klass.Get(),
+      soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Decode());
   CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
   throws_sfield.SetObject<false>(
-      klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+      klass.Get(),
+      soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Decode());
 
   {
     // Lock on klass is released. Lock new class object.
@@ -4300,7 +4387,7 @@
     }
 
     StackHandleScope<1> hs2(self);
-    Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String*>(name));
+    Handle<mirror::String> decoded_name = hs2.NewHandle(soa.Decode<mirror::String>(name));
     std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
                                                    decoded_name->ToModifiedUtf8().c_str()));
     CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name);
@@ -4310,9 +4397,9 @@
     CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
 
     CHECK_EQ(klass.Get()->GetInterfaces(),
-             soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+             soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Decode());
     CHECK_EQ(klass.Get()->GetThrows(),
-             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
+             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Decode());
   }
   return klass.Get();
 }
@@ -6267,7 +6354,7 @@
       // or interface methods in the IMT here they will not create extra conflicts since we compare
       // names and signatures in SetIMTRef.
       ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
-      const uint32_t imt_index = interface_method->GetImtIndex();
+      const uint32_t imt_index = ImTable::GetImtIndex(interface_method);
 
       // There is only any conflicts if all of the interface methods for an IMT slot don't have
       // the same implementation method, keep track of this to avoid creating a conflict table in
@@ -6321,7 +6408,7 @@
         }
         DCHECK(implementation_method != nullptr);
         ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
-        const uint32_t imt_index = interface_method->GetImtIndex();
+        const uint32_t imt_index = ImTable::GetImtIndex(interface_method);
         if (!imt[imt_index]->IsRuntimeMethod() ||
             imt[imt_index] == unimplemented_method ||
             imt[imt_index] == imt_conflict_method) {
@@ -6780,7 +6867,7 @@
         auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
         MethodNameAndSignatureComparator interface_name_comparator(
             interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
-        uint32_t imt_index = interface_method->GetImtIndex();
+        uint32_t imt_index = ImTable::GetImtIndex(interface_method);
         ArtMethod** imt_ptr = &out_imt[imt_index];
         // 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
@@ -7523,6 +7610,7 @@
                                            Handle<mirror::DexCache> dex_cache) {
   DCHECK(dex_cache.Get() != nullptr);
   mirror::String* resolved = dex_cache->GetResolvedString(string_idx);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr) {
     return resolved;
   }
@@ -7565,6 +7653,7 @@
                                         Handle<mirror::ClassLoader> class_loader) {
   DCHECK(dex_cache.Get() != nullptr);
   mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved == nullptr) {
     Thread* self = Thread::Current();
     const char* descriptor = dex_file.StringByTypeIdx(type_idx);
@@ -7603,6 +7692,7 @@
   DCHECK(dex_cache.Get() != nullptr);
   // Check for hit in the dex cache.
   ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
     DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
     if (kResolveMode == ClassLinker::kForceICCECheck) {
@@ -7806,6 +7896,7 @@
                                                        Handle<mirror::DexCache> dex_cache,
                                                        Handle<mirror::ClassLoader> class_loader) {
   ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
     DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
     return resolved;
@@ -7838,6 +7929,7 @@
                                     bool is_static) {
   DCHECK(dex_cache.Get() != nullptr);
   ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr) {
     return resolved;
   }
@@ -7880,6 +7972,7 @@
                                        Handle<mirror::ClassLoader> class_loader) {
   DCHECK(dex_cache.Get() != nullptr);
   ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+  Thread::PoisonObjectPointersIfDebug();
   if (resolved != nullptr) {
     return resolved;
   }
@@ -7905,6 +7998,68 @@
   return resolved;
 }
 
+mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
+                                                   uint32_t proto_idx,
+                                                   Handle<mirror::DexCache> dex_cache,
+                                                   Handle<mirror::ClassLoader> class_loader) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK(dex_cache.Get() != nullptr);
+
+  mirror::MethodType* resolved = dex_cache->GetResolvedMethodType(proto_idx);
+  if (resolved != nullptr) {
+    return resolved;
+  }
+
+  Thread* const self = Thread::Current();
+  StackHandleScope<4> hs(self);
+
+  // First resolve the return type.
+  const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx);
+  Handle<mirror::Class> return_type(hs.NewHandle(
+      ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader)));
+  if (return_type.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  // Then resolve the argument types.
+  //
+  // TODO: Is there a better way to figure out the number of method arguments
+  // other than by looking at the shorty ?
+  const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1;
+
+  mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+  mirror::Class* array_of_class = FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args)));
+  if (method_params.Get() == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  DexFileParameterIterator it(dex_file, proto_id);
+  int32_t i = 0;
+  MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(nullptr);
+  for (; it.HasNext(); it.Next()) {
+    const uint16_t type_idx = it.GetTypeIdx();
+    param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader));
+    if (param_class.Get() == nullptr) {
+      DCHECK(self->IsExceptionPending());
+      return nullptr;
+    }
+
+    method_params->Set(i++, param_class.Get());
+  }
+
+  DCHECK(!it.HasNext());
+
+  Handle<mirror::MethodType> type = hs.NewHandle(
+      mirror::MethodType::Create(self, return_type, method_params));
+  dex_cache->SetResolvedMethodType(proto_idx, type.Get());
+
+  return type.Get();
+}
+
 const char* ClassLinker::MethodShorty(uint32_t method_idx,
                                       ArtMethod* referrer,
                                       uint32_t* length) {
@@ -7920,7 +8075,7 @@
   explicit DumpClassVisitor(int flags) : flags_(flags) {}
 
   bool operator()(mirror::Class* klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-    klass->DumpClass(LOG(ERROR), flags_);
+    klass->DumpClass(LOG_STREAM(ERROR), flags_);
     return true;
   }
 
@@ -8061,6 +8216,8 @@
     "[Ljava/lang/reflect/Constructor;",
     "[Ljava/lang/reflect/Field;",
     "[Ljava/lang/reflect/Method;",
+    "Ljava/lang/invoke/MethodHandleImpl;",
+    "Ljava/lang/invoke/MethodType;",
     "Ljava/lang/ClassLoader;",
     "Ljava/lang/Throwable;",
     "Ljava/lang/ClassNotFoundException;",
@@ -8163,7 +8320,7 @@
 
   // Create PathClassLoader.
   Handle<mirror::Class> h_path_class_class = hs.NewHandle(
-      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader));
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
   Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
       h_path_class_class->AllocObject(self));
   DCHECK(h_path_class_loader.Get() != nullptr);
@@ -8180,7 +8337,7 @@
                                "Ljava/lang/ClassLoader;");
   DCHECK(parent_field != nullptr);
   mirror::Object* boot_cl =
-      soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
   parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
 
   // Make it a global ref and return.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0a46e2e..df7fb61 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -49,8 +49,10 @@
   class ClassLoader;
   class DexCache;
   class DexCachePointerArray;
+  class DexCacheMethodHandlesTest_Open_Test;
   class DexCacheTest_Open_Test;
   class IfTable;
+  class MethodType;
   template<class T> class ObjectArray;
   class StackTraceElement;
 }  // namespace mirror
@@ -99,6 +101,8 @@
     kJavaLangReflectConstructorArrayClass,
     kJavaLangReflectFieldArrayClass,
     kJavaLangReflectMethodArrayClass,
+    kJavaLangInvokeMethodHandleImpl,
+    kJavaLangInvokeMethodType,
     kJavaLangClassLoader,
     kJavaLangThrowable,
     kJavaLangClassNotFoundException,
@@ -359,6 +363,15 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
+  // Resolve a method type with a given ID from the DexFile, storing
+  // the result in the DexCache.
+  mirror::MethodType* ResolveMethodType(const DexFile& dex_file,
+                                        uint32_t proto_idx,
+                                        Handle<mirror::DexCache> dex_cache,
+                                        Handle<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+
   // Get shorty from method index without resolution. Used to do handlerization.
   const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1172,11 +1185,13 @@
   PointerSize image_pointer_size_;
 
   class FindVirtualMethodHolderVisitor;
+  friend struct CompilationHelper;  // For Compile in ImageTest.
   friend class ImageDumper;  // for DexLock
   friend class ImageWriter;  // for GetClassRoots
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class JniInternalTest;  // for GetRuntimeQuickGenericJniStub
   ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName);  // for DexLock, and RegisterDexFileLocked
+  ART_FRIEND_TEST(mirror::DexCacheMethodHandlesTest, Open);  // for AllocDexCache
   ART_FRIEND_TEST(mirror::DexCacheTest, Open);  // for AllocDexCache
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
 };
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5e0ee6f..451b752 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -28,12 +28,13 @@
 #include "experimental_flags.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "gc/heap.h"
-#include "mirror/abstract_method.h"
 #include "mirror/accessible_object.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache.h"
 #include "mirror/executable.h"
 #include "mirror/field.h"
+#include "mirror/method_type.h"
+#include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
@@ -41,7 +42,7 @@
 #include "mirror/stack_trace_element.h"
 #include "mirror/string-inl.h"
 #include "handle_scope-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -450,6 +451,14 @@
   };
 };
 
+class ClassLinkerMethodHandlesTest : public ClassLinkerTest {
+ protected:
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    CommonRuntimeTest::SetUpRuntimeOptions(options);
+    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
+  }
+};
+
 struct CheckOffset {
   size_t cpp_offset;
   const char* java_name;
@@ -649,10 +658,12 @@
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
+    addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
     addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings");
@@ -697,22 +708,37 @@
 struct ExecutableOffsets : public CheckOffsets<mirror::Executable> {
   ExecutableOffsets() : CheckOffsets<mirror::Executable>(
       false, "Ljava/lang/reflect/Executable;") {
+    addOffset(OFFSETOF_MEMBER(mirror::Executable, access_flags_), "accessFlags");
+    addOffset(OFFSETOF_MEMBER(mirror::Executable, art_method_), "artMethod");
+    addOffset(OFFSETOF_MEMBER(mirror::Executable, declaring_class_), "declaringClass");
+    addOffset(OFFSETOF_MEMBER(mirror::Executable, declaring_class_of_overridden_method_),
+              "declaringClassOfOverriddenMethod");
+    addOffset(OFFSETOF_MEMBER(mirror::Executable, dex_method_index_), "dexMethodIndex");
     addOffset(OFFSETOF_MEMBER(mirror::Executable, has_real_parameter_data_),
               "hasRealParameterData");
     addOffset(OFFSETOF_MEMBER(mirror::Executable, parameters_), "parameters");
   };
 };
 
-struct AbstractMethodOffsets : public CheckOffsets<mirror::AbstractMethod> {
-  AbstractMethodOffsets() : CheckOffsets<mirror::AbstractMethod>(
-      false, "Ljava/lang/reflect/AbstractMethod;") {
-    addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, access_flags_), "accessFlags");
-    addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, art_method_), "artMethod");
-    addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, declaring_class_), "declaringClass");
-    addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, declaring_class_of_overridden_method_),
-              "declaringClassOfOverriddenMethod");
-    addOffset(OFFSETOF_MEMBER(mirror::AbstractMethod, dex_method_index_), "dexMethodIndex");
-  };
+struct MethodTypeOffsets : public CheckOffsets<mirror::MethodType> {
+  MethodTypeOffsets() : CheckOffsets<mirror::MethodType>(
+      false, "Ljava/lang/invoke/MethodType;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodType, form_), "form");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodType, method_descriptor_), "methodDescriptor");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodType, p_types_), "ptypes");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodType, r_type_), "rtype");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodType, wrap_alt_), "wrapAlt");
+  }
+};
+
+struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> {
+  MethodHandleImplOffsets() : CheckOffsets<mirror::MethodHandleImpl>(
+      false, "Ljava/lang/invoke/MethodHandle;") {
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, art_field_or_method_), "artFieldOrMethod");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, as_type_cache_), "asTypeCache");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, handle_kind_), "handleKind");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandleImpl, method_type_), "type");
+  }
 };
 
 // C++ fields must exactly match the fields in the Java classes. If this fails,
@@ -733,7 +759,8 @@
   EXPECT_TRUE(AccessibleObjectOffsets().Check());
   EXPECT_TRUE(FieldOffsets().Check());
   EXPECT_TRUE(ExecutableOffsets().Check());
-  EXPECT_TRUE(AbstractMethodOffsets().Check());
+  EXPECT_TRUE(MethodTypeOffsets().Check());
+  EXPECT_TRUE(MethodHandleImplOffsets().Check());
 }
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -760,7 +787,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Nested"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Nested"))));
 
   mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader);
   ASSERT_TRUE(outer != nullptr);
@@ -794,7 +821,7 @@
 
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MyClass"))));
   AssertNonExistentClass("LMyClass;");
   mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
   ASSERT_TRUE(MyClass != nullptr);
@@ -920,9 +947,9 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader_1(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MyClass"))));
   Handle<mirror::ClassLoader> class_loader_2(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MyClass"))));
   mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1);
   mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2);
   EXPECT_TRUE(MyClass_1 != nullptr);
@@ -934,7 +961,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Statics"))));
   Handle<mirror::Class> statics(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
   class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
@@ -1011,7 +1038,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<6> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Interfaces"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Interfaces"))));
   Handle<mirror::Class> I(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
   Handle<mirror::Class> J(
@@ -1080,7 +1107,7 @@
   const DexFile* dex_file = GetFirstDexFile(jclass_loader);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
   mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
   ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize);
   ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize);
@@ -1210,7 +1237,7 @@
 
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Statics"))));
   Handle<mirror::Class> statics(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
 
@@ -1225,7 +1252,7 @@
 
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Statics"))));
 
   // java.lang.Object is a bootstrap class.
   Handle<mirror::Class> jlo_class(
@@ -1269,7 +1296,6 @@
                                                 old_dex_file->Size(),
                                                 location->ToModifiedUtf8(),
                                                 0u,
-                                                nullptr,
                                                 nullptr));
   {
     WriterMutexLock mu(soa.Self(), *class_linker->DexLock());
@@ -1278,4 +1304,59 @@
   }
 }
 
+TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<7> hs(soa.Self());
+
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MethodTypes"))));
+  Handle<mirror::Class> method_types(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
+  class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
+
+  ArtMethod* method1 = method_types->FindVirtualMethod("method1",
+                                                       "(Ljava/lang/String;)Ljava/lang/String;",
+                                                       kRuntimePointerSize);
+
+  const DexFile& dex_file = *(method1->GetDexFile());
+  Handle<mirror::DexCache> dex_cache = hs.NewHandle(
+      class_linker_->FindDexCache(Thread::Current(), dex_file));
+
+  const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex());
+
+  // This is the MethodType corresponding to the prototype of
+  // String MethodTypes# method1(String).
+  // Its RType = Ljava/lang/String;
+  // Its PTypes = { Ljava/lang/String; }
+  Handle<mirror::MethodType> method1_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+
+  // Assert that the method type was resolved successfully.
+  ASSERT_TRUE(method1_type.Get() != nullptr);
+
+  // Assert that the return type and the method arguments are as we expect.
+  Handle<mirror::Class> string_class(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader)));
+  ASSERT_EQ(string_class.Get(), method1_type->GetRType());
+  ASSERT_EQ(string_class.Get(), method1_type->GetPTypes()->Get(0));
+
+  // Resolve the method type again and assert that we get back the same value.
+  Handle<mirror::MethodType> method1_type2 = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+  ASSERT_EQ(method1_type.Get(), method1_type2.Get());
+
+  // Resolve the MethodType associated with a different method signature
+  // and assert it's different.
+  ArtMethod* method2 = method_types->FindVirtualMethod(
+      "method2",
+      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+  const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex());
+  Handle<mirror::MethodType> method2_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader));
+
+  ASSERT_TRUE(method1_type.Get() != method2_type.Get());
+}
+
 }  // namespace art
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 55ff73d..0600876 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -113,6 +113,10 @@
   classes_.back().Insert(GcRoot<mirror::Class>(klass));
 }
 
+void ClassTable::InsertWithoutLocks(mirror::Class* klass) {
+  classes_.back().Insert(GcRoot<mirror::Class>(klass));
+}
+
 void ClassTable::InsertWithHash(mirror::Class* klass, size_t hash) {
   WriterMutexLock mu(Thread::Current(), lock_);
   classes_.back().InsertWithHash(GcRoot<mirror::Class>(klass), hash);
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 66c241f..8c91806 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -163,6 +163,8 @@
   }
 
  private:
+  void InsertWithoutLocks(mirror::Class* klass) NO_THREAD_SAFETY_ANALYSIS;
+
   // Lock to guard inserting and removing.
   mutable ReaderWriterMutex lock_;
   // We have a vector to help prevent dirty pages after the zygote forks by calling FreezeSnapshot.
@@ -171,6 +173,8 @@
   // loader which may not be owned by the class loader must be held strongly live. Also dex caches
   // are held live to prevent them being unloading once they have classes in them.
   std::vector<GcRoot<mirror::Object>> strong_roots_ GUARDED_BY(lock_);
+
+  friend class ImageWriter;  // for InsertWithoutLocks.
 };
 
 }  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 7234952..eda1ddd 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -47,7 +47,7 @@
 #include "os.h"
 #include "primitive.h"
 #include "runtime-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "well_known_classes.h"
 
@@ -58,7 +58,7 @@
   setenv("ANDROID_LOG_TAGS", "*:e", 1);
 
   art::InitLogging(argv);
-  LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
+  LOG(INFO) << "Running main() from common_runtime_test.cc...";
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();
 }
@@ -76,9 +76,10 @@
   file_.reset(new File(fd, GetFilename(), true));
 }
 
-ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) {
-  filename_ = other.GetFilename();
-  filename_ += suffix;
+ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix)
+    : ScratchFile(other.GetFilename() + suffix) {}
+
+ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) {
   int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
   CHECK_NE(-1, fd);
   file_.reset(new File(fd, GetFilename(), true));
@@ -90,6 +91,18 @@
   file_.reset(file);
 }
 
+ScratchFile::ScratchFile(ScratchFile&& other) {
+  *this = std::move(other);
+}
+
+ScratchFile& ScratchFile::operator=(ScratchFile&& other) {
+  if (GetFile() != other.GetFile()) {
+    std::swap(filename_, other.filename_);
+    std::swap(file_, other.file_);
+  }
+  return *this;
+}
+
 ScratchFile::~ScratchFile() {
   Unlink();
 }
@@ -498,12 +511,12 @@
 
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(jclass_loader));
+      soa.Decode<mirror::ClassLoader>(jclass_loader));
 
   DCHECK_EQ(class_loader->GetClass(),
-            soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader));
+            soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader).Decode());
   DCHECK_EQ(class_loader->GetParent()->GetClass(),
-            soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader));
+            soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader).Decode());
 
   // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
   // We need to get the DexPathList and loop through it.
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index b2090b7..a7948e4 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -40,8 +40,14 @@
  public:
   ScratchFile();
 
+  explicit ScratchFile(const std::string& filename);
+
   ScratchFile(const ScratchFile& other, const char* suffix);
 
+  explicit ScratchFile(ScratchFile&& other);
+
+  ScratchFile& operator=(ScratchFile&& other);
+
   explicit ScratchFile(File* file);
 
   ~ScratchFile();
@@ -113,8 +119,7 @@
 
   std::string GetTestDexFileName(const char* name) const;
 
-  std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
 
   std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6ed44fc..0206cae 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -48,7 +48,7 @@
 #include "mirror/throwable.h"
 #include "reflection.h"
 #include "safe_map.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "handle_scope-inl.h"
@@ -390,7 +390,8 @@
     return nullptr;
   }
 
-  mirror::Class* java_lang_Thread = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+  ObjPtr<mirror::Class> java_lang_Thread =
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread);
   if (!java_lang_Thread->IsAssignableFrom(thread_peer->GetClass())) {
     // This isn't a thread.
     *error = JDWP::ERR_INVALID_THREAD;
@@ -431,21 +432,22 @@
     return JDWP::JT_CLASS_OBJECT;
   }
   {
-    mirror::Class* thread_class = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+    ObjPtr<mirror::Class> thread_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread);
     if (thread_class->IsAssignableFrom(c)) {
       return JDWP::JT_THREAD;
     }
   }
   {
-    mirror::Class* thread_group_class =
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+    ObjPtr<mirror::Class> thread_group_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ThreadGroup);
     if (thread_group_class->IsAssignableFrom(c)) {
       return JDWP::JT_THREAD_GROUP;
     }
   }
   {
-    mirror::Class* class_loader_class =
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader);
+    ObjPtr<mirror::Class> class_loader_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader);
     if (class_loader_class->IsAssignableFrom(c)) {
       return JDWP::JT_CLASS_LOADER;
     }
@@ -1946,7 +1948,8 @@
   }
   {
     ScopedObjectAccessUnchecked soa(Thread::Current());
-    mirror::Class* java_lang_String = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_String);
+    ObjPtr<mirror::Class> java_lang_String =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_String);
     if (!java_lang_String->IsAssignableFrom(obj->GetClass())) {
       // This isn't a string.
       return JDWP::ERR_INVALID_STRING;
@@ -2014,7 +2017,7 @@
     expandBufAddObjectId(pReply, JDWP::ObjectId(0));
     error = JDWP::ERR_NONE;
   } else if (error == JDWP::ERR_NONE) {
-    mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread);
     CHECK(c != nullptr);
     ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_group);
     CHECK(f != nullptr);
@@ -2038,7 +2041,8 @@
     *error = JDWP::ERR_INVALID_OBJECT;
     return nullptr;
   }
-  mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+  ObjPtr<mirror::Class> c =
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ThreadGroup);
   CHECK(c != nullptr);
   if (!c->IsAssignableFrom(thread_group->GetClass())) {
     // This is not a java.lang.ThreadGroup.
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 03223b0..0af086c 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -52,6 +52,10 @@
 
 namespace art {
 
+static constexpr OatDexFile* kNoOatDexFile = nullptr;
+
+const char* DexFile::kClassesDex = "classes.dex";
+
 const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
 const uint8_t DexFile::kDexMagicVersions[DexFile::kNumDexVersions][DexFile::kDexVersionLen] = {
   {'0', '3', '5', '\0'},
@@ -118,64 +122,6 @@
   return false;
 }
 
-bool DexFile::Open(const char* filename,
-                   const char* location,
-                   bool verify_checksum,
-                   std::string* error_msg,
-                   std::vector<std::unique_ptr<const DexFile>>* dex_files) {
-  ScopedTrace trace(std::string("Open dex file ") + location);
-  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
-  uint32_t magic;
-  File fd = OpenAndReadMagic(filename, &magic, error_msg);
-  if (fd.Fd() == -1) {
-    DCHECK(!error_msg->empty());
-    return false;
-  }
-  if (IsZipMagic(magic)) {
-    return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
-  }
-  if (IsDexMagic(magic)) {
-    std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
-                                                              location,
-                                                              /* verify */ true,
-                                                              verify_checksum,
-                                                              error_msg));
-    if (dex_file.get() != nullptr) {
-      dex_files->push_back(std::move(dex_file));
-      return true;
-    } else {
-      return false;
-    }
-  }
-  *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
-  return false;
-}
-
-static bool ContainsClassesDex(int fd, const char* filename) {
-  std::string error_msg;
-  std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, filename, &error_msg));
-  if (zip_archive.get() == nullptr) {
-    return false;
-  }
-  std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex, &error_msg));
-  return (zip_entry.get() != nullptr);
-}
-
-bool DexFile::MaybeDex(const char* filename) {
-  uint32_t magic;
-  std::string error_msg;
-  File fd = OpenAndReadMagic(filename, &magic, &error_msg);
-  if (fd.Fd() == -1) {
-    return false;
-  }
-  if (IsZipMagic(magic)) {
-    return ContainsClassesDex(fd.Release(), filename);
-  } else if (IsDexMagic(magic)) {
-    return true;
-  }
-  return false;
-}
-
 int DexFile::GetPermissions() const {
   if (mem_map_.get() == nullptr) {
     return 0;
@@ -206,7 +152,9 @@
   }
 }
 
-std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size,
+
+std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base,
+                                             size_t size,
                                              const std::string& location,
                                              uint32_t location_checksum,
                                              const OatDexFile* oat_dex_file,
@@ -214,118 +162,78 @@
                                              bool verify_checksum,
                                              std::string* error_msg) {
   ScopedTrace trace(std::string("Open dex file from RAM ") + location);
-  std::unique_ptr<const DexFile> dex_file = OpenMemory(base,
-                                                       size,
-                                                       location,
-                                                       location_checksum,
-                                                       nullptr,
-                                                       oat_dex_file,
-                                                       error_msg);
-  if (dex_file == nullptr) {
-    return nullptr;
-  }
-
-  if (verify && !DexFileVerifier::Verify(dex_file.get(),
-                                         dex_file->Begin(),
-                                         dex_file->Size(),
-                                         location.c_str(),
-                                         verify_checksum,
-                                         error_msg)) {
-    return nullptr;
-  }
-  return dex_file;
+  return OpenCommon(base,
+                    size,
+                    location,
+                    location_checksum,
+                    oat_dex_file,
+                    verify,
+                    verify_checksum,
+                    error_msg);
 }
 
 std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
                                              uint32_t location_checksum,
-                                             std::unique_ptr<MemMap> mem_map,
+                                             std::unique_ptr<MemMap> map,
                                              bool verify,
                                              bool verify_checksum,
                                              std::string* error_msg) {
   ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
-  std::unique_ptr<const DexFile> dex_file = OpenMemory(location,
-                                                       location_checksum,
-                                                       std::move(mem_map),
-                                                       error_msg);
-  if (dex_file == nullptr) {
-    return nullptr;
-  }
-
-  if (verify && !DexFileVerifier::Verify(dex_file.get(),
-                                         dex_file->Begin(),
-                                         dex_file->Size(),
-                                         location.c_str(),
-                                         verify_checksum,
-                                         error_msg)) {
-    return nullptr;
+  CHECK(map.get() != nullptr);
+  std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+                                                 map->Size(),
+                                                 location,
+                                                 location_checksum,
+                                                 kNoOatDexFile,
+                                                 verify,
+                                                 verify_checksum,
+                                                 error_msg);
+  if (dex_file != nullptr) {
+    dex_file->mem_map_.reset(map.release());
   }
   return dex_file;
 }
 
-std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
-                                                 const char* location,
-                                                 bool verify,
-                                                 bool verify_checksum,
-                                                 std::string* error_msg) {
-  ScopedTrace trace(std::string("Open dex file ") + location);
-  CHECK(location != nullptr);
-  std::unique_ptr<MemMap> map;
-  {
-    File delayed_close(fd, /* check_usage */ false);
-    struct stat sbuf;
-    memset(&sbuf, 0, sizeof(sbuf));
-    if (fstat(fd, &sbuf) == -1) {
-      *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location, strerror(errno));
-      return nullptr;
-    }
-    if (S_ISDIR(sbuf.st_mode)) {
-      *error_msg = StringPrintf("Attempt to mmap directory '%s'", location);
-      return nullptr;
-    }
-    size_t length = sbuf.st_size;
-    map.reset(MemMap::MapFile(length,
-                              PROT_READ,
-                              MAP_PRIVATE,
-                              fd,
-                              0,
-                              /*low_4gb*/false,
-                              location,
-                              error_msg));
-    if (map == nullptr) {
-      DCHECK(!error_msg->empty());
-      return nullptr;
+bool DexFile::Open(const char* filename,
+                   const std::string& location,
+                   bool verify_checksum,
+                   std::string* error_msg,
+                   std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
+  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
+  uint32_t magic;
+  File fd = OpenAndReadMagic(filename, &magic, error_msg);
+  if (fd.Fd() == -1) {
+    DCHECK(!error_msg->empty());
+    return false;
+  }
+  if (IsZipMagic(magic)) {
+    return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
+  }
+  if (IsDexMagic(magic)) {
+    std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
+                                                              location,
+                                                              /* verify */ true,
+                                                              verify_checksum,
+                                                              error_msg));
+    if (dex_file.get() != nullptr) {
+      dex_files->push_back(std::move(dex_file));
+      return true;
+    } else {
+      return false;
     }
   }
-
-  if (map->Size() < sizeof(DexFile::Header)) {
-    *error_msg = StringPrintf(
-        "DexFile: failed to open dex file '%s' that is too short to have a header", location);
-    return nullptr;
-  }
-
-  const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
-
-  std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
-                                                     dex_header->checksum_,
-                                                     std::move(map),
-                                                     error_msg));
-  if (dex_file.get() == nullptr) {
-    *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
-                              error_msg->c_str());
-    return nullptr;
-  }
-
-  if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
-                                         location,
-                                         verify_checksum,
-                                         error_msg)) {
-    return nullptr;
-  }
-
-  return dex_file;
+  *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
+  return false;
 }
 
-const char* DexFile::kClassesDex = "classes.dex";
+std::unique_ptr<const DexFile> DexFile::OpenDex(int fd,
+                                                const std::string& location,
+                                                bool verify_checksum,
+                                                std::string* error_msg) {
+  ScopedTrace trace("Open dex file " + std::string(location));
+  return OpenFile(fd, location, true /* verify */, verify_checksum, error_msg);
+}
 
 bool DexFile::OpenZip(int fd,
                       const std::string& location,
@@ -339,32 +247,83 @@
     DCHECK(!error_msg->empty());
     return false;
   }
-  return DexFile::OpenFromZip(*zip_archive, location, verify_checksum, error_msg, dex_files);
+  return DexFile::OpenAllDexFilesFromZip(*zip_archive,
+                                         location,
+                                         verify_checksum,
+                                         error_msg,
+                                         dex_files);
 }
 
-std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
-                                                   uint32_t location_checksum,
-                                                   std::unique_ptr<MemMap> mem_map,
-                                                   std::string* error_msg) {
-  return OpenMemory(mem_map->Begin(),
-                    mem_map->Size(),
-                    location,
-                    location_checksum,
-                    std::move(mem_map),
-                    nullptr,
-                    error_msg);
+std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
+                                                 const std::string& location,
+                                                 bool verify,
+                                                 bool verify_checksum,
+                                                 std::string* error_msg) {
+  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
+  CHECK(!location.empty());
+  std::unique_ptr<MemMap> map;
+  {
+    File delayed_close(fd, /* check_usage */ false);
+    struct stat sbuf;
+    memset(&sbuf, 0, sizeof(sbuf));
+    if (fstat(fd, &sbuf) == -1) {
+      *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
+                                strerror(errno));
+      return nullptr;
+    }
+    if (S_ISDIR(sbuf.st_mode)) {
+      *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
+      return nullptr;
+    }
+    size_t length = sbuf.st_size;
+    map.reset(MemMap::MapFile(length,
+                              PROT_READ,
+                              MAP_PRIVATE,
+                              fd,
+                              0,
+                              /*low_4gb*/false,
+                              location.c_str(),
+                              error_msg));
+    if (map == nullptr) {
+      DCHECK(!error_msg->empty());
+      return nullptr;
+    }
+  }
+
+  if (map->Size() < sizeof(DexFile::Header)) {
+    *error_msg = StringPrintf(
+        "DexFile: failed to open dex file '%s' that is too short to have a header",
+        location.c_str());
+    return nullptr;
+  }
+
+  const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
+
+  std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+                                                 map->Size(),
+                                                 location,
+                                                 dex_header->checksum_,
+                                                 kNoOatDexFile,
+                                                 verify,
+                                                 verify_checksum,
+                                                 error_msg);
+  if (dex_file != nullptr) {
+    dex_file->mem_map_.reset(map.release());
+  }
+
+  return dex_file;
 }
 
-std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive,
-                                             const char* entry_name,
-                                             const std::string& location,
-                                             bool verify_checksum,
-                                             std::string* error_msg,
-                                             ZipOpenErrorCode* error_code) {
+std::unique_ptr<const DexFile> DexFile::OpenOneDexFileFromZip(const ZipArchive& zip_archive,
+                                                              const char* entry_name,
+                                                              const std::string& location,
+                                                              bool verify_checksum,
+                                                              std::string* error_msg,
+                                                              ZipOpenErrorCode* error_code) {
   ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
   CHECK(!location.empty());
   std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
-  if (zip_entry.get() == nullptr) {
+  if (zip_entry == nullptr) {
     *error_code = ZipOpenErrorCode::kEntryNotFound;
     return nullptr;
   }
@@ -374,32 +333,38 @@
     return nullptr;
   }
   std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
-  if (map.get() == nullptr) {
+  if (map == nullptr) {
     *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
                               error_msg->c_str());
     *error_code = ZipOpenErrorCode::kExtractToMemoryError;
     return nullptr;
   }
-  std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
-                                                     zip_entry->GetCrc32(),
-                                                     std::move(map),
-                                                     error_msg));
+  VerifyResult verify_result;
+  std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
+                                                 map->Size(),
+                                                 location,
+                                                 zip_entry->GetCrc32(),
+                                                 kNoOatDexFile,
+                                                 /* verify */ true,
+                                                 verify_checksum,
+                                                 error_msg,
+                                                 &verify_result);
   if (dex_file == nullptr) {
-    *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
-                              error_msg->c_str());
-    *error_code = ZipOpenErrorCode::kDexFileError;
+    if (verify_result == VerifyResult::kVerifyNotAttempted) {
+      *error_code = ZipOpenErrorCode::kDexFileError;
+    } else {
+      *error_code = ZipOpenErrorCode::kVerifyError;
+    }
     return nullptr;
   }
+  dex_file->mem_map_.reset(map.release());
   if (!dex_file->DisableWrite()) {
     *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
     *error_code = ZipOpenErrorCode::kMakeReadOnlyError;
     return nullptr;
   }
   CHECK(dex_file->IsReadOnly()) << location;
-  if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
-                               location.c_str(),
-                               verify_checksum,
-                               error_msg)) {
+  if (verify_result != VerifyResult::kVerifySucceeded) {
     *error_code = ZipOpenErrorCode::kVerifyError;
     return nullptr;
   }
@@ -413,16 +378,20 @@
 // seems an excessive number.
 static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
 
-bool DexFile::OpenFromZip(const ZipArchive& zip_archive,
-                          const std::string& location,
-                          bool verify_checksum,
-                          std::string* error_msg,
-                          std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+bool DexFile::OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
+                                     const std::string& location,
+                                     bool verify_checksum,
+                                     std::string* error_msg,
+                                     std::vector<std::unique_ptr<const DexFile>>* dex_files) {
   ScopedTrace trace("Dex file open from Zip " + std::string(location));
   DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
   ZipOpenErrorCode error_code;
-  std::unique_ptr<const DexFile> dex_file(
-      Open(zip_archive, kClassesDex, location, verify_checksum, error_msg, &error_code));
+  std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
+                                                                kClassesDex,
+                                                                location,
+                                                                verify_checksum,
+                                                                error_msg,
+                                                                &error_code));
   if (dex_file.get() == nullptr) {
     return false;
   } else {
@@ -437,8 +406,12 @@
     for (size_t i = 1; ; ++i) {
       std::string name = GetMultiDexClassesDexName(i);
       std::string fake_location = GetMultiDexLocation(i, location.c_str());
-      std::unique_ptr<const DexFile> next_dex_file(
-          Open(zip_archive, name.c_str(), fake_location, verify_checksum, error_msg, &error_code));
+      std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
+                                                                         name.c_str(),
+                                                                         fake_location,
+                                                                         verify_checksum,
+                                                                         error_msg,
+                                                                         &error_code));
       if (next_dex_file.get() == nullptr) {
         if (error_code != ZipOpenErrorCode::kEntryNotFound) {
           LOG(WARNING) << error_msg;
@@ -464,35 +437,58 @@
   }
 }
 
-
-std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base,
-                                                   size_t size,
-                                                   const std::string& location,
-                                                   uint32_t location_checksum,
-                                                   std::unique_ptr<MemMap> mem_map,
-                                                   const OatDexFile* oat_dex_file,
-                                                   std::string* error_msg) {
-  DCHECK(base != nullptr);
-  DCHECK_NE(size, 0U);
-  CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned
-  std::unique_ptr<DexFile> dex_file(
-      new DexFile(base, size, location, location_checksum, std::move(mem_map), oat_dex_file));
+std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
+                                             size_t size,
+                                             const std::string& location,
+                                             uint32_t location_checksum,
+                                             const OatDexFile* oat_dex_file,
+                                             bool verify,
+                                             bool verify_checksum,
+                                             std::string* error_msg,
+                                             VerifyResult* verify_result) {
+  if (verify_result != nullptr) {
+    *verify_result = VerifyResult::kVerifyNotAttempted;
+  }
+  std::unique_ptr<DexFile> dex_file(new DexFile(base,
+                                                size,
+                                                location,
+                                                location_checksum,
+                                                oat_dex_file));
+  if (dex_file == nullptr) {
+    *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
+                              error_msg->c_str());
+    return nullptr;
+  }
   if (!dex_file->Init(error_msg)) {
     dex_file.reset();
+    return nullptr;
   }
-  return std::unique_ptr<const DexFile>(dex_file.release());
+  if (verify && !DexFileVerifier::Verify(dex_file.get(),
+                                         dex_file->Begin(),
+                                         dex_file->Size(),
+                                         location.c_str(),
+                                         verify_checksum,
+                                         error_msg)) {
+    if (verify_result != nullptr) {
+      *verify_result = VerifyResult::kVerifyFailed;
+    }
+    return nullptr;
+  }
+  if (verify_result != nullptr) {
+    *verify_result = VerifyResult::kVerifySucceeded;
+  }
+  return dex_file;
 }
 
-DexFile::DexFile(const uint8_t* base, size_t size,
+DexFile::DexFile(const uint8_t* base,
+                 size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
-                 std::unique_ptr<MemMap> mem_map,
                  const OatDexFile* oat_dex_file)
     : begin_(base),
       size_(size),
       location_(location),
       location_checksum_(location_checksum),
-      mem_map_(std::move(mem_map)),
       header_(reinterpret_cast<const Header*>(base)),
       string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
       type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
@@ -577,6 +573,34 @@
   return nullptr;
 }
 
+uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def,
+                                     uint32_t method_idx) const {
+  const uint8_t* class_data = GetClassData(class_def);
+  CHECK(class_data != nullptr);
+  ClassDataItemIterator it(*this, class_data);
+  // Skip fields
+  while (it.HasNextStaticField()) {
+    it.Next();
+  }
+  while (it.HasNextInstanceField()) {
+    it.Next();
+  }
+  while (it.HasNextDirectMethod()) {
+    if (it.GetMemberIndex() == method_idx) {
+      return it.GetMethodCodeItemOffset();
+    }
+    it.Next();
+  }
+  while (it.HasNextVirtualMethod()) {
+    if (it.GetMemberIndex() == method_idx) {
+      return it.GetMethodCodeItemOffset();
+    }
+    it.Next();
+  }
+  LOG(FATAL) << "Unable to find method " << method_idx;
+  UNREACHABLE();
+}
+
 const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass,
                                              const DexFile::StringId& name,
                                              const DexFile::TypeId& type) const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 97c2596..29b8c3a 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -397,15 +397,9 @@
   // Return true if the checksum could be found, false otherwise.
   static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
 
-  // Opens .dex files found in the container, guessing the container format based on file extension.
-  static bool Open(const char* filename,
-                   const char* location,
-                   bool verify_checksum,
-                   std::string* error_msg,
-                   std::vector<std::unique_ptr<const DexFile>>* dex_files);
-
   // Opens .dex file, backed by existing memory
-  static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
+  static std::unique_ptr<const DexFile> Open(const uint8_t* base,
+                                             size_t size,
                                              const std::string& location,
                                              uint32_t location_checksum,
                                              const OatDexFile* oat_dex_file,
@@ -421,16 +415,25 @@
                                              bool verify_checksum,
                                              std::string* error_msg);
 
-  // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
-  // If this function returns false, Open will not succeed. The inverse is not true, however.
-  static bool MaybeDex(const char* filename);
+  // Opens all .dex files found in the file, guessing the container format based on file extension.
+  static bool Open(const char* filename,
+                   const std::string& location,
+                   bool verify_checksum,
+                   std::string* error_msg,
+                   std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
-  // Open all classesXXX.dex files from a zip archive.
-  static bool OpenFromZip(const ZipArchive& zip_archive,
-                          const std::string& location,
-                          bool verify_checksum,
-                          std::string* error_msg,
-                          std::vector<std::unique_ptr<const DexFile>>* dex_files);
+  // Open a single dex file from an fd.
+  static std::unique_ptr<const DexFile> OpenDex(int fd,
+                                                const std::string& location,
+                                                bool verify_checksum,
+                                                std::string* error_msg);
+
+  // Opens dex files from within a .jar, .zip, or .apk file
+  static bool OpenZip(int fd,
+                      const std::string& location,
+                      bool verify_checksum,
+                      std::string* error_msg,
+                      std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
   // Closes a .dex file.
   virtual ~DexFile();
@@ -587,6 +590,9 @@
                              const DexFile::StringId& name,
                              const DexFile::TypeId& type) const;
 
+  uint32_t FindCodeItemOffset(const DexFile::ClassDef& class_def,
+                              uint32_t dex_method_idx) const;
+
   // Returns the declaring class descriptor string of a field id.
   const char* GetFieldDeclaringClassDescriptor(const FieldId& field_id) const {
     const DexFile::TypeId& type_id = GetTypeId(field_id.class_idx_);
@@ -1011,20 +1017,12 @@
   static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right);
 
  private:
-  // Opens a .dex file
   static std::unique_ptr<const DexFile> OpenFile(int fd,
-                                                 const char* location,
+                                                 const std::string& location,
                                                  bool verify,
                                                  bool verify_checksum,
                                                  std::string* error_msg);
 
-  // Opens dex files from within a .jar, .zip, or .apk file
-  static bool OpenZip(int fd,
-                      const std::string& location,
-                      bool verify_checksum,
-                      std::string* error_msg,
-                      std::vector<std::unique_ptr<const DexFile>>* dex_files);
-
   enum class ZipOpenErrorCode {  // private
     kNoError,
     kEntryNotFound,
@@ -1034,20 +1032,38 @@
     kVerifyError
   };
 
+  // Open all classesXXX.dex files from a zip archive.
+  static bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
+                                     const std::string& location,
+                                     bool verify_checksum,
+                                     std::string* error_msg,
+                                     std::vector<std::unique_ptr<const DexFile>>* dex_files);
+
   // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
   // return.
-  static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive,
-                                             const char* entry_name,
+  static std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive,
+                                                              const char* entry_name,
+                                                              const std::string& location,
+                                                              bool verify_checksum,
+                                                              std::string* error_msg,
+                                                              ZipOpenErrorCode* error_code);
+
+  enum class VerifyResult {  // private
+    kVerifyNotAttempted,
+    kVerifySucceeded,
+    kVerifyFailed
+  };
+
+  static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
+                                             size_t size,
                                              const std::string& location,
+                                             uint32_t location_checksum,
+                                             const OatDexFile* oat_dex_file,
+                                             bool verify,
                                              bool verify_checksum,
                                              std::string* error_msg,
-                                             ZipOpenErrorCode* error_code);
+                                             VerifyResult* verify_result = nullptr);
 
-  // Opens a .dex file at the given address backed by a MemMap
-  static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
-                                                   uint32_t location_checksum,
-                                                   std::unique_ptr<MemMap> mem_map,
-                                                   std::string* error_msg);
 
   // Opens a .dex file at the given address, optionally backed by a MemMap
   static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
@@ -1058,10 +1074,10 @@
                                                    const OatDexFile* oat_dex_file,
                                                    std::string* error_msg);
 
-  DexFile(const uint8_t* base, size_t size,
+  DexFile(const uint8_t* base,
+          size_t size,
           const std::string& location,
           uint32_t location_checksum,
-          std::unique_ptr<MemMap> mem_map,
           const OatDexFile* oat_dex_file);
 
   // Top-level initializer that calls other Init methods.
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index c6c87fd..5763479 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -255,7 +255,7 @@
   }
 
   mirror::Class* annotation_member_class =
-      soa.Decode<mirror::Class*>(WellKnownClasses::libcore_reflect_AnnotationMember);
+      soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember).Decode();
   mirror::Class* annotation_member_array_class =
       class_linker->FindArrayClass(self, &annotation_member_class);
   if (annotation_member_array_class == nullptr) {
@@ -572,7 +572,7 @@
   *annotation_ptr = annotation;
 
   if (result_style == DexFile::kAllObjects && primitive_type != Primitive::kPrimVoid) {
-    element_object = BoxPrimitive(primitive_type, annotation_value->value_);
+    element_object = BoxPrimitive(primitive_type, annotation_value->value_).Decode();
     set_object = true;
   }
 
@@ -782,7 +782,7 @@
   ScopedObjectAccessUnchecked soa(self);
   StackHandleScope<2> hs(self);
   Handle<mirror::Class> annotation_array_class(hs.NewHandle(
-      soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array)));
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array)));
   if (annotation_set == nullptr) {
     return mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), 0);
   }
@@ -840,7 +840,7 @@
   ScopedObjectAccessUnchecked soa(self);
   StackHandleScope<1> hs(self);
   mirror::Class* annotation_array_class =
-      soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array).Decode();
   mirror::Class* annotation_array_array_class =
       Runtime::Current()->GetClassLinker()->FindArrayClass(self, &annotation_array_class);
   if (annotation_array_array_class == nullptr) {
@@ -1027,6 +1027,66 @@
                                               annotation_class);
 }
 
+bool GetParametersMetadataForMethod(ArtMethod* method,
+                                    MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+                                    MutableHandle<mirror::IntArray>* access_flags) {
+  const DexFile::AnnotationSetItem::AnnotationSetItem* annotation_set =
+      FindAnnotationSetForMethod(method);
+  if (annotation_set == nullptr) {
+    return false;
+  }
+
+  const DexFile* dex_file = method->GetDexFile();
+  const DexFile::AnnotationItem* annotation_item =
+      SearchAnnotationSet(*dex_file,
+                          annotation_set,
+                          "Ldalvik/annotation/MethodParameters;",
+                          DexFile::kDexVisibilitySystem);
+  if (annotation_item == nullptr) {
+    return false;
+  }
+
+  StackHandleScope<5> hs(Thread::Current());
+
+  // Extract the parameters' names String[].
+  mirror::Class* string_class = mirror::String::GetJavaLangString();
+  Handle<mirror::Class> string_array_class(hs.NewHandle(
+      Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
+  if (UNLIKELY(string_array_class.Get() == nullptr)) {
+    return false;
+  }
+
+  Handle<mirror::Class> klass = hs.NewHandle(method->GetDeclaringClass());
+  Handle<mirror::Object> names_obj =
+      hs.NewHandle(GetAnnotationValue(klass,
+                                      annotation_item,
+                                      "names",
+                                      string_array_class,
+                                      DexFile::kDexAnnotationArray));
+  if (names_obj.Get() == nullptr) {
+    return false;
+  }
+
+  // Extract the parameters' access flags int[].
+  Handle<mirror::Class> int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass()));
+  if (UNLIKELY(int_array_class.Get() == nullptr)) {
+    return false;
+  }
+  Handle<mirror::Object> access_flags_obj =
+      hs.NewHandle(GetAnnotationValue(klass,
+                                      annotation_item,
+                                      "accessFlags",
+                                      int_array_class,
+                                      DexFile::kDexAnnotationArray));
+  if (access_flags_obj.Get() == nullptr) {
+    return false;
+  }
+
+  names->Assign(names_obj.Get()->AsObjectArray<mirror::String>());
+  access_flags->Assign(access_flags_obj.Get()->AsIntArray());
+  return true;
+}
+
 mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
   if (annotation_set == nullptr) {
diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h
index 7b4e856..c66c5bd 100644
--- a/runtime/dex_file_annotations.h
+++ b/runtime/dex_file_annotations.h
@@ -30,6 +30,7 @@
 class ArtField;
 class ArtMethod;
 class ClassLinker;
+template<class T> class MutableHandle;
 
 namespace annotations {
 
@@ -58,6 +59,10 @@
                                                 uint32_t parameter_idx,
                                                 Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
+bool GetParametersMetadataForMethod(ArtMethod* method,
+                                    MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+                                    MutableHandle<mirror::IntArray>* access_flags)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class,
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 6a06177..8e1501f 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -24,8 +24,9 @@
 #include "dex_file-inl.h"
 #include "mem_map.h"
 #include "os.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
+#include "utils.h"
 
 namespace art {
 
@@ -37,65 +38,13 @@
   ASSERT_TRUE(dex.get() != nullptr);
 }
 
-static const uint8_t kBase64Map[256] = {
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
-  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  // NOLINT
-   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,  // NOLINT
-  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
-   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  // NOLINT
-   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,  // NOLINT
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255
-};
-
-static inline std::vector<uint8_t> DecodeBase64(const char* src) {
-  std::vector<uint8_t> tmp;
-  uint32_t t = 0, y = 0;
-  int g = 3;
-  for (size_t i = 0; src[i] != '\0'; ++i) {
-    uint8_t c = kBase64Map[src[i] & 0xFF];
-    if (c == 255) continue;
-    // the final = symbols are read and used to trim the remaining bytes
-    if (c == 254) {
-      c = 0;
-      // prevent g < 0 which would potentially allow an overflow later
-      if (--g < 0) {
-        return std::vector<uint8_t>();
-      }
-    } else if (g != 3) {
-      // we only allow = to be at the end
-        return std::vector<uint8_t>();
-    }
-    t = (t << 6) | c;
-    if (++y == 4) {
-      tmp.push_back((t >> 16) & 255);
-      if (g > 1) {
-        tmp.push_back((t >> 8) & 255);
-      }
-      if (g > 2) {
-        tmp.push_back(t & 255);
-      }
-      y = t = 0;
-    }
-  }
-  if (y != 0) {
-    return std::vector<uint8_t>();
-  }
-  return tmp;
+static inline std::vector<uint8_t> DecodeBase64Vec(const char* src) {
+  std::vector<uint8_t> res;
+  size_t size;
+  std::unique_ptr<uint8_t[]> data(DecodeBase64(src, &size));
+  res.resize(size);
+  memcpy(res.data(), data.get(), size);
+  return res;
 }
 
 // Although this is the same content logically as the Nested test dex,
@@ -163,10 +112,59 @@
   "AGNsYXNzZXMuZGV4VVQFAANkDaFXdXgLAAEE5AMBAASIEwAAUEsFBgAAAAABAAEAUQAAAEUAAAAA"
   "AA==";
 
+static const char kRawZipClassesDexPresent[] =
+  "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VXdXgL"
+  "AAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMDQ0WY"
+  "iRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEIEEcA"
+  "cS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu4IOa"
+  "wczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhHIykL"
+  "LinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkVIGpA"
+  "Yc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEBAAAg"
+  "AgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEABIgTAABQ"
+  "SwUGAAAAAAEAAQBRAAAAdgEAAAAA";
+
+static const char kRawZipClassesDexAbsent[] =
+  "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAAOABwAbm90Y2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VX"
+  "dXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMD"
+  "Q0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEI"
+  "EEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu"
+  "4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhH"
+  "IykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkV"
+  "IGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEB"
+  "AAAgAgAADgAYAAAAAAAAAAAAoIEAAAAAbm90Y2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEA"
+  "BIgTAABQSwUGAAAAAAEAAQBUAAAAeQEAAAAA";
+
+static const char kRawZipThreeDexFiles[] =
+  "UEsDBBQAAAAIAP1WN0ms99lIMQEAACACAAAMABwAY2xhc3NlczIuZGV4VVQJAAOtbOVXrWzlV3V4"
+  "CwABBOQDAQAEiBMAAEtJreAyMLZg2GHVfXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NF"
+  "mIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBAAGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBH"
+  "AHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiDHWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCD"
+  "msHMwGaTmZdZYsfA5uObmJlnzSDkk5VYlqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMp"
+  "Cy4pysxLt2ZgyQUqAzmYj4EZTIL909jA8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBq"
+  "QGHOKAARB4UbkwLETFA8MEPVgMKCQQGiBhxOUPWgeAYAUEsDBBQAAAAIAABXN0ms99lIMQEAACAC"
+  "AAAMABwAY2xhc3NlczMuZGV4VVQJAAOvbOVXr2zlV3V4CwABBOQDAQAEiBMAAEtJreAyMLZg2GHV"
+  "fXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NFmIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBA"
+  "AGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBHAHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiD"
+  "HWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCDmsHMwGaTmZdZYsfA5uObmJlnzSDkk5VY"
+  "lqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMpCy4pysxLt2ZgyQUqAzmYj4EZTIL909jA"
+  "8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBqQGHOKAARB4UbkwLETFA8MEPVgMKCQQGi"
+  "BhxOUPWgeAYAUEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj"
+  "5VetbOVXdXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQC"
+  "EwNDAQMDQ0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGx"
+  "GxAHAnEIEEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8"
+  "UFGgP6Fu4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYx"
+  "MIX5MAhHIykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHC"
+  "mg0pvBkVIGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACAD9VjdJ"
+  "rPfZSDEBAAAgAgAADAAYAAAAAAAAAAAAoIEAAAAAY2xhc3NlczIuZGV4VVQFAAOtbOVXdXgLAAEE"
+  "5AMBAASIEwAAUEsBAh4DFAAAAAgAAFc3Saz32UgxAQAAIAIAAAwAGAAAAAAAAAAAAKCBdwEAAGNs"
+  "YXNzZXMzLmRleFVUBQADr2zlV3V4CwABBOQDAQAEiBMAAFBLAQIeAxQAAAAIANVRN0ms99lIMQEA"
+  "ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA"
+  "AFBLBQYAAAAAAwADAPUAAABkBAAAAAA=";
+
 static void DecodeAndWriteDexFile(const char* base64, const char* location) {
   // decode base64
   CHECK(base64 != nullptr);
-  std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+  std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
   CHECK_NE(dex_bytes.size(), 0u);
 
   // write to provided file
@@ -180,29 +178,43 @@
   }
 }
 
-static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
-                                                        const char* location) {
+static bool OpenDexFilesBase64(const char* base64,
+                               const char* location,
+                               std::vector<std::unique_ptr<const DexFile>>* dex_files,
+                               std::string* error_msg) {
   DecodeAndWriteDexFile(base64, location);
 
-  // read dex file
+  // read dex file(s)
   ScopedObjectAccess soa(Thread::Current());
   static constexpr bool kVerifyChecksum = true;
-  std::string error_msg;
   std::vector<std::unique_ptr<const DexFile>> tmp;
-  bool success = DexFile::Open(location, location, kVerifyChecksum, &error_msg, &tmp);
+  bool success = DexFile::Open(location, location, kVerifyChecksum, error_msg, &tmp);
+  if (success) {
+    for (std::unique_ptr<const DexFile>& dex_file : tmp) {
+      EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
+      EXPECT_TRUE(dex_file->IsReadOnly());
+    }
+    *dex_files = std::move(tmp);
+  }
+  return success;
+}
+
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+                                                        const char* location) {
+  // read dex files.
+  std::string error_msg;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  bool success = OpenDexFilesBase64(base64, location, &dex_files, &error_msg);
   CHECK(success) << error_msg;
-  EXPECT_EQ(1U, tmp.size());
-  std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
-  EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
-  EXPECT_TRUE(dex_file->IsReadOnly());
-  return dex_file;
+  EXPECT_EQ(1U, dex_files.size());
+  return std::move(dex_files[0]);
 }
 
 static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
                                                                 const char* location,
                                                                 uint32_t location_checksum) {
   CHECK(base64 != nullptr);
-  std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+  std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
   CHECK_NE(dex_bytes.size(), 0u);
 
   std::string error_message;
@@ -507,4 +519,31 @@
   EXPECT_EQ(":classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes8.dex"));
 }
 
+TEST_F(DexFileTest, ZipOpenClassesPresent) {
+  ScratchFile tmp;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ASSERT_TRUE(OpenDexFilesBase64(kRawZipClassesDexPresent, tmp.GetFilename().c_str(), &dex_files,
+                                 &error_msg));
+  EXPECT_EQ(dex_files.size(), 1u);
+}
+
+TEST_F(DexFileTest, ZipOpenClassesAbsent) {
+  ScratchFile tmp;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ASSERT_FALSE(OpenDexFilesBase64(kRawZipClassesDexAbsent, tmp.GetFilename().c_str(), &dex_files,
+                                  &error_msg));
+  EXPECT_EQ(dex_files.size(), 0u);
+}
+
+TEST_F(DexFileTest, ZipOpenThreeDexFiles) {
+  ScratchFile tmp;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ASSERT_TRUE(OpenDexFilesBase64(kRawZipThreeDexFiles, tmp.GetFilename().c_str(), &dex_files,
+                                 &error_msg));
+  EXPECT_EQ(dex_files.size(), 3u);
+}
+
 }  // namespace art
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 71c0ad9..e392870 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -27,36 +27,12 @@
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
 #include "leb128.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
+#include "utils.h"
 
 namespace art {
 
-static const uint8_t kBase64Map[256] = {
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
-  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  // NOLINT
-   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,  // NOLINT
-  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
-   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  // NOLINT
-   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,  // NOLINT
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255
-};
-
 // Make the Dex file version 37.
 static void MakeDexVersion37(DexFile* dex_file) {
   size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
@@ -64,52 +40,6 @@
   *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
 }
 
-static inline std::unique_ptr<uint8_t[]> DecodeBase64(const char* src, size_t* dst_size) {
-  std::vector<uint8_t> tmp;
-  uint32_t t = 0, y = 0;
-  int g = 3;
-  for (size_t i = 0; src[i] != '\0'; ++i) {
-    uint8_t c = kBase64Map[src[i] & 0xFF];
-    if (c == 255) continue;
-    // the final = symbols are read and used to trim the remaining bytes
-    if (c == 254) {
-      c = 0;
-      // prevent g < 0 which would potentially allow an overflow later
-      if (--g < 0) {
-        *dst_size = 0;
-        return nullptr;
-      }
-    } else if (g != 3) {
-      // we only allow = to be at the end
-      *dst_size = 0;
-      return nullptr;
-    }
-    t = (t << 6) | c;
-    if (++y == 4) {
-      tmp.push_back((t >> 16) & 255);
-      if (g > 1) {
-        tmp.push_back((t >> 8) & 255);
-      }
-      if (g > 2) {
-        tmp.push_back(t & 255);
-      }
-      y = t = 0;
-    }
-  }
-  if (y != 0) {
-    *dst_size = 0;
-    return nullptr;
-  }
-  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
-  if (dst_size != nullptr) {
-    *dst_size = tmp.size();
-  } else {
-    *dst_size = 0;
-  }
-  std::copy(tmp.begin(), tmp.end(), dst.get());
-  return dst;
-}
-
 static void FixUpChecksum(uint8_t* dex_file) {
   DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
   uint32_t expected_size = header->file_size_;
@@ -123,7 +53,7 @@
 class DexFileVerifierTest : public CommonRuntimeTest {
  protected:
   DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
-    return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
+    return new DexFile(dex_bytes, length, "tmp", 0, nullptr);
   }
 
   void VerifyModification(const char* dex_file_base64_content,
@@ -131,7 +61,7 @@
                           std::function<void(DexFile*)> f,
                           const char* expected_error) {
     size_t length;
-    std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length);
+    std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_base64_content, &length));
     CHECK(dex_bytes != nullptr);
     // Note: `dex_file` will be destroyed before `dex_bytes`.
     std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
@@ -1704,7 +1634,7 @@
 
 TEST_F(DexFileVerifierTest, Checksum) {
   size_t length;
-  std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(kGoodTestDex, &length);
+  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &length));
   CHECK(dex_bytes != nullptr);
   // Note: `dex_file` will be destroyed before `dex_bytes`.
   std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc
index 2681ad0..9f28c8c 100644
--- a/runtime/dex_method_iterator_test.cc
+++ b/runtime/dex_method_iterator_test.cc
@@ -19,7 +19,7 @@
 #include "base/stl_util.h"
 #include "common_runtime_test.h"
 #include "oat_file.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 14110c2..99b8805 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -26,6 +26,8 @@
 #include "dex_file.h"
 #include "entrypoints/quick/callee_save_frame.h"
 #include "handle_scope-inl.h"
+#include "imt_conflict_table.h"
+#include "imtable-inl.h"
 #include "indirect_reference_table.h"
 #include "invoke_type.h"
 #include "jni_internal.h"
@@ -612,7 +614,7 @@
       UNREACHABLE();
     }
     case kInterface: {
-      uint32_t imt_index = resolved_method->GetImtIndex();
+      uint32_t imt_index = ImTable::GetImtIndex(resolved_method);
       PointerSize pointer_size = class_linker->GetImagePointerSize();
       ArtMethod* imt_method = (*this_object)->GetClass()->GetImt(pointer_size)->
           Get(imt_index, pointer_size);
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index fd9ffbd..38ee468 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -33,7 +33,7 @@
 #include "nth_caller_visitor.h"
 #include "oat_quick_method_header.h"
 #include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -160,12 +160,12 @@
       } else {
         JValue jv;
         jv.SetJ(args.at(i).j);
-        mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
+        mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv).Decode();
         if (val == nullptr) {
           CHECK(soa.Self()->IsExceptionPending());
           return zero;
         }
-        soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set<false>(i, val);
+        soa.Decode<mirror::ObjectArray<mirror::Object>>(args_jobj)->Set<false>(i, val);
       }
     }
   }
@@ -187,13 +187,13 @@
       return zero;
     } else {
       ArtMethod* interface_method =
-          soa.Decode<mirror::Method*>(interface_method_jobj)->GetArtMethod();
+          soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
       // This can cause thread suspension.
       PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
       mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size);
-      mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
+      ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
       JValue result_unboxed;
-      if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
+      if (!UnboxPrimitiveForResult(result_ref.Decode(), result_type, &result_unboxed)) {
         DCHECK(soa.Self()->IsExceptionPending());
         return zero;
       }
@@ -207,9 +207,9 @@
       bool declares_exception = false;
       {
         ScopedAssertNoThreadSuspension ants(__FUNCTION__);
-        mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
+        ObjPtr<mirror::Object> rcvr = soa.Decode<mirror::Object>(rcvr_jobj);
         mirror::Class* proxy_class = rcvr->GetClass();
-        mirror::Method* interface_method = soa.Decode<mirror::Method*>(interface_method_jobj);
+        ObjPtr<mirror::Method> interface_method = soa.Decode<mirror::Method>(interface_method_jobj);
         ArtMethod* proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(
             interface_method->GetArtMethod(), kRuntimePointerSize);
         auto virtual_methods = proxy_class->GetVirtualMethodsSlice(kRuntimePointerSize);
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index 22226c1..fd23ced 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -18,7 +18,7 @@
 #include "base/logging.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 5b9d03b..343343f 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -36,7 +36,7 @@
       // specialized visitor that will show whether a method is Quick or Shadow.
     } else {
       LOG(INFO) << "Deopting:";
-      self->Dump(LOG(INFO));
+      self->Dump(LOG_STREAM(INFO));
     }
   }
 
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 76b5456..446e343 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -169,22 +169,33 @@
                                     HandleScope* handle_scope)
     // TODO: NO_THREAD_SAFETY_ANALYSIS as GoToRunnable() is NO_THREAD_SAFETY_ANALYSIS
     NO_THREAD_SAFETY_ANALYSIS {
-  GoToRunnable(self);
+  bool critical_native = called->IsAnnotatedWithCriticalNative();
+  bool fast_native = called->IsAnnotatedWithFastNative();
+  bool normal_native = !critical_native && !fast_native;
+
+  // @Fast and @CriticalNative do not do a state transition.
+  if (LIKELY(normal_native)) {
+    GoToRunnable(self);
+  }
   // We need the mutator lock (i.e., calling GoToRunnable()) before accessing the shorty or the
   // locked object.
   jobject locked = called->IsSynchronized() ? handle_scope->GetHandle(0).ToJObject() : nullptr;
   char return_shorty_char = called->GetShorty()[0];
   if (return_shorty_char == 'L') {
     if (locked != nullptr) {
+      DCHECK(normal_native) << " @FastNative and synchronize is not supported";
       UnlockJniSynchronizedMethod(locked, self);
     }
     return reinterpret_cast<uint64_t>(JniMethodEndWithReferenceHandleResult(
         result.l, saved_local_ref_cookie, self));
   } else {
     if (locked != nullptr) {
+      DCHECK(normal_native) << " @FastNative and synchronize is not supported";
       UnlockJniSynchronizedMethod(locked, self);  // Must decode before pop.
     }
-    PopLocalReferences(saved_local_ref_cookie, self);
+    if (LIKELY(!critical_native)) {
+      PopLocalReferences(saved_local_ref_cookie, self);
+    }
     switch (return_shorty_char) {
       case 'F': {
         if (kRuntimeISA == kX86) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 3c6f807..c52bc8e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,6 +23,8 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
+#include "imt_conflict_table.h"
+#include "imtable-inl.h"
 #include "interpreter/interpreter.h"
 #include "linear_alloc.h"
 #include "method_reference.h"
@@ -34,7 +36,7 @@
 #include "oat_quick_method_header.h"
 #include "quick_exception_handler.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "debugger.h"
 
@@ -832,7 +834,7 @@
 void BuildQuickArgumentVisitor::FixupReferences() {
   // Fixup any references which may have changed.
   for (const auto& pair : references_) {
-    pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+    pair.second->Assign(soa_->Decode<mirror::Object>(pair.first).Decode());
     soa_->Env()->DeleteLocalRef(pair.first);
   }
 }
@@ -924,7 +926,7 @@
 void RememberForGcArgumentVisitor::FixupReferences() {
   // Fixup any references which may have changed.
   for (const auto& pair : references_) {
-    pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+    pair.second->Assign(soa_->Decode<mirror::Object>(pair.first).Decode());
     soa_->Env()->DeleteLocalRef(pair.first);
   }
 }
@@ -1625,7 +1627,8 @@
 
 class ComputeGenericJniFrameSize FINAL : public ComputeNativeCallFrameSize {
  public:
-  ComputeGenericJniFrameSize() : num_handle_scope_references_(0) {}
+  explicit ComputeGenericJniFrameSize(bool critical_native)
+    : num_handle_scope_references_(0), critical_native_(critical_native) {}
 
   // Lays out the callee-save frame. Assumes that the incorrect frame corresponding to RefsAndArgs
   // is at *m = sp. Will update to point to the bottom of the save frame.
@@ -1711,6 +1714,7 @@
 
  private:
   uint32_t num_handle_scope_references_;
+  const bool critical_native_;
 };
 
 uintptr_t ComputeGenericJniFrameSize::PushHandle(mirror::Object* /* ptr */) {
@@ -1720,6 +1724,11 @@
 
 void ComputeGenericJniFrameSize::WalkHeader(
     BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>* sm) {
+  // First 2 parameters are always excluded for @CriticalNative.
+  if (UNLIKELY(critical_native_)) {
+    return;
+  }
+
   // JNIEnv
   sm->AdvancePointer(nullptr);
 
@@ -1778,11 +1787,16 @@
 // of transitioning into native code.
 class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
  public:
-  BuildGenericJniFrameVisitor(Thread* self, bool is_static, const char* shorty, uint32_t shorty_len,
+  BuildGenericJniFrameVisitor(Thread* self,
+                              bool is_static,
+                              bool critical_native,
+                              const char* shorty,
+                              uint32_t shorty_len,
                               ArtMethod*** sp)
      : QuickArgumentVisitor(*sp, is_static, shorty, shorty_len),
-       jni_call_(nullptr, nullptr, nullptr, nullptr), sm_(&jni_call_) {
-    ComputeGenericJniFrameSize fsc;
+       jni_call_(nullptr, nullptr, nullptr, nullptr, critical_native),
+       sm_(&jni_call_) {
+    ComputeGenericJniFrameSize fsc(critical_native);
     uintptr_t* start_gpr_reg;
     uint32_t* start_fpr_reg;
     uintptr_t* start_stack_arg;
@@ -1793,11 +1807,14 @@
 
     jni_call_.Reset(start_gpr_reg, start_fpr_reg, start_stack_arg, handle_scope_);
 
-    // jni environment is always first argument
-    sm_.AdvancePointer(self->GetJniEnv());
+    // First 2 parameters are always excluded for CriticalNative methods.
+    if (LIKELY(!critical_native)) {
+      // jni environment is always first argument
+      sm_.AdvancePointer(self->GetJniEnv());
 
-    if (is_static) {
-      sm_.AdvanceHandleScope((**sp)->GetDeclaringClass());
+      if (is_static) {
+        sm_.AdvanceHandleScope((**sp)->GetDeclaringClass());
+      }  // else "this" reference is already handled by QuickArgumentVisitor.
     }
   }
 
@@ -1822,8 +1839,11 @@
   class FillJniCall FINAL : public FillNativeCall {
    public:
     FillJniCall(uintptr_t* gpr_regs, uint32_t* fpr_regs, uintptr_t* stack_args,
-                HandleScope* handle_scope) : FillNativeCall(gpr_regs, fpr_regs, stack_args),
-                                             handle_scope_(handle_scope), cur_entry_(0) {}
+                HandleScope* handle_scope, bool critical_native)
+      : FillNativeCall(gpr_regs, fpr_regs, stack_args),
+                       handle_scope_(handle_scope),
+        cur_entry_(0),
+        critical_native_(critical_native) {}
 
     uintptr_t PushHandle(mirror::Object* ref) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1839,12 +1859,17 @@
       while (cur_entry_ < expected_slots) {
         handle_scope_->GetMutableHandle(cur_entry_++).Assign(nullptr);
       }
-      DCHECK_NE(cur_entry_, 0U);
+
+      if (!critical_native_) {
+        // Non-critical natives have at least the self class (jclass) or this (jobject).
+        DCHECK_NE(cur_entry_, 0U);
+      }
     }
 
    private:
     HandleScope* handle_scope_;
     size_t cur_entry_;
+    const bool critical_native_;
   };
 
   HandleScope* handle_scope_;
@@ -1924,7 +1949,12 @@
 extern "C" void* artFindNativeMethod(Thread* self);
 #endif
 
-uint64_t artQuickGenericJniEndJNIRef(Thread* self, uint32_t cookie, jobject l, jobject lock) {
+static uint64_t artQuickGenericJniEndJNIRef(Thread* self,
+                                            uint32_t cookie,
+                                            bool fast_native ATTRIBUTE_UNUSED,
+                                            jobject l,
+                                            jobject lock) {
+  // TODO: add entrypoints for @FastNative returning objects.
   if (lock != nullptr) {
     return reinterpret_cast<uint64_t>(JniMethodEndWithReferenceSynchronized(l, cookie, lock, self));
   } else {
@@ -1932,11 +1962,19 @@
   }
 }
 
-void artQuickGenericJniEndJNINonRef(Thread* self, uint32_t cookie, jobject lock) {
+static void artQuickGenericJniEndJNINonRef(Thread* self,
+                                           uint32_t cookie,
+                                           bool fast_native,
+                                           jobject lock) {
   if (lock != nullptr) {
     JniMethodEndSynchronized(cookie, lock, self);
+    // Ignore "fast_native" here because synchronized functions aren't very fast.
   } else {
-    JniMethodEnd(cookie, self);
+    if (UNLIKELY(fast_native)) {
+      JniMethodFastEnd(cookie, self);
+    } else {
+      JniMethodEnd(cookie, self);
+    }
   }
 }
 
@@ -1958,9 +1996,17 @@
   DCHECK(called->IsNative()) << PrettyMethod(called, true);
   uint32_t shorty_len = 0;
   const char* shorty = called->GetShorty(&shorty_len);
+  bool critical_native = called->IsAnnotatedWithCriticalNative();
+  bool fast_native = called->IsAnnotatedWithFastNative();
+  bool normal_native = !critical_native && !fast_native;
 
   // Run the visitor and update sp.
-  BuildGenericJniFrameVisitor visitor(self, called->IsStatic(), shorty, shorty_len, &sp);
+  BuildGenericJniFrameVisitor visitor(self,
+                                      called->IsStatic(),
+                                      critical_native,
+                                      shorty,
+                                      shorty_len,
+                                      &sp);
   {
     ScopedAssertNoThreadSuspension sants(__FUNCTION__);
     visitor.VisitArguments();
@@ -1973,20 +2019,30 @@
 
   self->VerifyStack();
 
-  // Start JNI, save the cookie.
   uint32_t cookie;
-  if (called->IsSynchronized()) {
-    cookie = JniMethodStartSynchronized(visitor.GetFirstHandleScopeJObject(), self);
-    if (self->IsExceptionPending()) {
-      self->PopHandleScope();
-      // A negative value denotes an error.
-      return GetTwoWordFailureValue();
+  uint32_t* sp32;
+  // Skip calling JniMethodStart for @CriticalNative.
+  if (LIKELY(!critical_native)) {
+    // Start JNI, save the cookie.
+    if (called->IsSynchronized()) {
+      DCHECK(normal_native) << " @FastNative and synchronize is not supported";
+      cookie = JniMethodStartSynchronized(visitor.GetFirstHandleScopeJObject(), self);
+      if (self->IsExceptionPending()) {
+        self->PopHandleScope();
+        // A negative value denotes an error.
+        return GetTwoWordFailureValue();
+      }
+    } else {
+      if (fast_native) {
+        cookie = JniMethodFastStart(self);
+      } else {
+        DCHECK(normal_native);
+        cookie = JniMethodStart(self);
+      }
     }
-  } else {
-    cookie = JniMethodStart(self);
+    sp32 = reinterpret_cast<uint32_t*>(sp);
+    *(sp32 - 1) = cookie;
   }
-  uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
-  *(sp32 - 1) = cookie;
 
   // Retrieve the stored native code.
   void* nativeCode = called->GetEntryPointFromJni();
@@ -2007,12 +2063,15 @@
     if (nativeCode == nullptr) {
       DCHECK(self->IsExceptionPending());    // There should be an exception pending now.
 
-      // End JNI, as the assembly will move to deliver the exception.
-      jobject lock = called->IsSynchronized() ? visitor.GetFirstHandleScopeJObject() : nullptr;
-      if (shorty[0] == 'L') {
-        artQuickGenericJniEndJNIRef(self, cookie, nullptr, lock);
-      } else {
-        artQuickGenericJniEndJNINonRef(self, cookie, lock);
+      // @CriticalNative calls do not need to call back into JniMethodEnd.
+      if (LIKELY(!critical_native)) {
+        // End JNI, as the assembly will move to deliver the exception.
+        jobject lock = called->IsSynchronized() ? visitor.GetFirstHandleScopeJObject() : nullptr;
+        if (shorty[0] == 'L') {
+          artQuickGenericJniEndJNIRef(self, cookie, fast_native, nullptr, lock);
+        } else {
+          artQuickGenericJniEndJNINonRef(self, cookie, fast_native, lock);
+        }
       }
 
       return GetTwoWordFailureValue();
@@ -2182,7 +2241,8 @@
   if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
     // If the dex cache already resolved the interface method, look whether we have
     // a match in the ImtConflictTable.
-    ArtMethod* conflict_method = imt->Get(interface_method->GetImtIndex(), kRuntimePointerSize);
+    ArtMethod* conflict_method = imt->Get(ImTable::GetImtIndex(interface_method),
+                                          kRuntimePointerSize);
     if (LIKELY(conflict_method->IsRuntimeMethod())) {
       ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize);
       DCHECK(current_table != nullptr);
@@ -2234,7 +2294,7 @@
 
   // We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
   // We create a new table with the new pair { interface_method, method }.
-  uint32_t imt_index = interface_method->GetImtIndex();
+  uint32_t imt_index = ImTable::GetImtIndex(interface_method);
   ArtMethod* conflict_method = imt->Get(imt_index, kRuntimePointerSize);
   if (conflict_method->IsRuntimeMethod()) {
     ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index 54d2c35..5ddb9fa 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -28,6 +28,7 @@
     kNone           = 0x0000,
     kAgents         = 0x0001,  // 0b00000001
     kRuntimePlugins = 0x0002,  // 0b00000010
+    kMethodHandles  = 0x0004,  // 0b00000100
   };
 
   constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -74,6 +75,10 @@
     stream << (started ? "|" : "") << "kRuntimePlugins";
     started = true;
   }
+  if (e & ExperimentalFlags::kMethodHandles) {
+    stream << (started ? "|" : "") << "kMethodHandles";
+    started = true;
+  }
   if (!started) {
     stream << "kNone";
   }
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index f86921c..f9345b6 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -438,7 +438,7 @@
     // above.
     abort();
 #endif
-    self->DumpJavaStack(LOG(ERROR));
+    self->DumpJavaStack(LOG_STREAM(ERROR));
   }
 
   return false;  // Return false since we want to propagate the fault to the main signal handler.
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index 819cb85..67ab14c 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -23,7 +23,7 @@
 #include "handle_scope-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/string-inl.h"  // Strings are easiest to allocate
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_pool.h"
 #include "utils.h"
 
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 24a2c17..3b6750e 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -283,7 +283,7 @@
           << from_space->GetGcRetentionPolicy();
       LOG(INFO) << "ToSpace " << to_space->GetName() << " type "
           << to_space->GetGcRetentionPolicy();
-      heap->DumpSpaces(LOG(INFO));
+      heap->DumpSpaces(LOG_STREAM(INFO));
       LOG(FATAL) << "FATAL ERROR";
     }
   }
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 3649111..a968343 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -191,86 +191,6 @@
   }
 }
 
-template<size_t kAlignment>
-void SpaceBitmap<kAlignment>::WalkInstanceFields(SpaceBitmap<kAlignment>* visited,
-                                                 ObjectCallback* callback, mirror::Object* obj,
-                                                 mirror::Class* klass, void* arg)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // Visit fields of parent classes first.
-  mirror::Class* super = klass->GetSuperClass();
-  if (super != nullptr) {
-    WalkInstanceFields(visited, callback, obj, super, arg);
-  }
-  // Walk instance fields
-  for (ArtField& field : klass->GetIFields()) {
-    if (!field.IsPrimitiveType()) {
-      mirror::Object* value = field.GetObj(obj);
-      if (value != nullptr) {
-        WalkFieldsInOrder(visited, callback, value, arg);
-      }
-    }
-  }
-}
-
-template<size_t kAlignment>
-void SpaceBitmap<kAlignment>::WalkFieldsInOrder(SpaceBitmap<kAlignment>* visited,
-                                                ObjectCallback* callback, mirror::Object* obj,
-                                                void* arg) {
-  if (visited->Test(obj)) {
-    return;
-  }
-  // visit the object itself
-  (*callback)(obj, arg);
-  visited->Set(obj);
-  // Walk instance fields of all objects
-  mirror::Class* klass = obj->GetClass();
-  WalkInstanceFields(visited, callback, obj, klass, arg);
-  // Walk static fields of a Class
-  if (obj->IsClass()) {
-    for (ArtField& field : klass->GetSFields()) {
-      if (!field.IsPrimitiveType()) {
-        mirror::Object* value = field.GetObj(nullptr);
-        if (value != nullptr) {
-          WalkFieldsInOrder(visited, callback, value, arg);
-        }
-      }
-    }
-  } else if (obj->IsObjectArray()) {
-    // Walk elements of an object array
-    mirror::ObjectArray<mirror::Object>* obj_array = obj->AsObjectArray<mirror::Object>();
-    int32_t length = obj_array->GetLength();
-    for (int32_t i = 0; i < length; i++) {
-      mirror::Object* value = obj_array->Get(i);
-      if (value != nullptr) {
-        WalkFieldsInOrder(visited, callback, value, arg);
-      }
-    }
-  }
-}
-
-template<size_t kAlignment>
-void SpaceBitmap<kAlignment>::InOrderWalk(ObjectCallback* callback, void* arg) {
-  std::unique_ptr<SpaceBitmap<kAlignment>> visited(
-      Create("bitmap for in-order walk", reinterpret_cast<uint8_t*>(heap_begin_),
-             IndexToOffset(bitmap_size_ / sizeof(intptr_t))));
-  CHECK(bitmap_begin_ != nullptr);
-  CHECK(callback != nullptr);
-  uintptr_t end = Size() / sizeof(intptr_t);
-  for (uintptr_t i = 0; i < end; ++i) {
-    // Need uint for unsigned shift.
-    uintptr_t w = bitmap_begin_[i].LoadRelaxed();
-    if (UNLIKELY(w != 0)) {
-      uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
-      while (w != 0) {
-        const size_t shift = CTZ(w);
-        mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
-        WalkFieldsInOrder(visited.get(), callback, obj, arg);
-        w ^= (static_cast<uintptr_t>(1)) << shift;
-      }
-    }
-  }
-}
-
 template class SpaceBitmap<kObjectAlignment>;
 template class SpaceBitmap<kPageSize>;
 
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 576f9c7..296663a 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -133,11 +133,6 @@
   void Walk(ObjectCallback* callback, void* arg)
       REQUIRES_SHARED(Locks::heap_bitmap_lock_);
 
-  // Visits set bits with an in order traversal.  The callback is not permitted to change the bitmap
-  // bits or max during the traversal.
-  void InOrderWalk(ObjectCallback* callback, void* arg)
-      REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
-
   // 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.
@@ -202,15 +197,6 @@
   template<bool kSetBit>
   bool Modify(const mirror::Object* obj);
 
-  // For an unvisited object, visit it then all its children found via fields.
-  static void WalkFieldsInOrder(SpaceBitmap* visited, ObjectCallback* callback, mirror::Object* obj,
-                                void* arg) REQUIRES_SHARED(Locks::mutator_lock_);
-  // Walk instance fields of the given Class. Separate function to allow recursion on the super
-  // class.
-  static void WalkInstanceFields(SpaceBitmap<kAlignment>* visited, ObjectCallback* callback,
-                                 mirror::Object* obj, mirror::Class* klass, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Backing storage for bitmap.
   std::unique_ptr<MemMap> mem_map_;
 
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index dc4e312..0c84224 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -37,6 +37,9 @@
 #pragma GCC diagnostic ignored "-Wempty-body"
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #include "../../../external/dlmalloc/malloc.c"
+// Note: malloc.c uses a DEBUG define to drive debug code. This interferes with the DEBUG severity
+//       of libbase, so undefine it now.
+#undef DEBUG
 #pragma GCC diagnostic pop
 
 static void* art_heap_morecore(void* m, intptr_t increment) {
@@ -44,11 +47,11 @@
 }
 
 static void art_heap_corruption(const char* function) {
-  LOG(::art::FATAL) << "Corrupt heap detected in: " << function;
+  LOG(FATAL) << "Corrupt heap detected in: " << function;
 }
 
 static void art_heap_usage_error(const char* function, void* p) {
-  LOG(::art::FATAL) << "Incorrect use of function '" << function << "' argument " << p
+  LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p
       << " not expected";
 }
 
@@ -69,7 +72,7 @@
     int rc = madvise(start, length, MADV_DONTNEED);
     if (UNLIKELY(rc != 0)) {
       errno = rc;
-      PLOG(::art::FATAL) << "madvise failed during heap trimming";
+      PLOG(FATAL) << "madvise failed during heap trimming";
     }
     size_t* reclaimed = reinterpret_cast<size_t*>(arg);
     *reclaimed += length;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 2750fea..8b91075 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -32,7 +32,7 @@
 #include "intern_table.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "well_known_classes.h"
@@ -430,7 +430,7 @@
   TimingLogger::ScopedTiming split("FlipThreadRoots", GetTimings());
   if (kVerboseMode) {
     LOG(INFO) << "time=" << region_space_->Time();
-    region_space_->DumpNonFreeRegions(LOG(INFO));
+    region_space_->DumpNonFreeRegions(LOG_STREAM(INFO));
   }
   Thread* self = Thread::Current();
   Locks::mutator_lock_->AssertNotHeld(self);
@@ -447,7 +447,7 @@
   QuasiAtomic::ThreadFenceForConstructor();
   if (kVerboseMode) {
     LOG(INFO) << "time=" << region_space_->Time();
-    region_space_->DumpNonFreeRegions(LOG(INFO));
+    region_space_->DumpNonFreeRegions(LOG_STREAM(INFO));
     LOG(INFO) << "GC end of FlipThreadRoots";
   }
 }
@@ -1622,7 +1622,7 @@
       if (obj != nullptr) {
         LogFromSpaceRefHolder(obj, offset);
       }
-      ref->GetLockWord(false).Dump(LOG(INTERNAL_FATAL));
+      ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
       CHECK(false) << "Found from-space ref " << ref << " " << PrettyTypeOf(ref);
     } else {
       AssertToSpaceInvariantInNonMovingSpace(obj, ref);
@@ -1645,13 +1645,13 @@
   template <class MirrorType>
   void VisitRoot(mirror::Object** root)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    LOG(INTERNAL_FATAL) << "root=" << root << " ref=" << *root;
+    LOG(FATAL_WITHOUT_ABORT) << "root=" << root << " ref=" << *root;
   }
 
   template <class MirrorType>
   void VisitRoot(mirror::CompressedReference<MirrorType>* root)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    LOG(INTERNAL_FATAL) << "root=" << root << " ref=" << root->AsMirrorPtr();
+    LOG(FATAL_WITHOUT_ABORT) << "root=" << root << " ref=" << root->AsMirrorPtr();
   }
 };
 
@@ -1670,19 +1670,19 @@
         // No info.
       } else if (gc_root_source->HasArtField()) {
         ArtField* field = gc_root_source->GetArtField();
-        LOG(INTERNAL_FATAL) << "gc root in field " << field << " " << PrettyField(field);
+        LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " " << PrettyField(field);
         RootPrinter root_printer;
         field->VisitRoots(root_printer);
       } else if (gc_root_source->HasArtMethod()) {
         ArtMethod* method = gc_root_source->GetArtMethod();
-        LOG(INTERNAL_FATAL) << "gc root in method " << method << " " << PrettyMethod(method);
+        LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " " << PrettyMethod(method);
         RootPrinter root_printer;
         method->VisitRoots(root_printer, kRuntimePointerSize);
       }
-      ref->GetLockWord(false).Dump(LOG(INTERNAL_FATAL));
-      region_space_->DumpNonFreeRegions(LOG(INTERNAL_FATAL));
-      PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL);
-      MemMap::DumpMaps(LOG(INTERNAL_FATAL), true);
+      ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+      region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
+      PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+      MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
       CHECK(false) << "Found from-space ref " << ref << " " << PrettyTypeOf(ref);
     } else {
       AssertToSpaceInvariantInNonMovingSpace(nullptr, ref);
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index d866106..6d2f009 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -139,7 +139,7 @@
           REQUIRES_SHARED(Locks::mutator_lock_) {
         // Marking a large object, make sure its aligned as a sanity check.
         if (!IsAligned<kPageSize>(ref)) {
-          Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+          Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
           LOG(FATAL) << ref;
         }
       };
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index cbc4dc1..b89d99c 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -41,7 +41,7 @@
 #include "mark_sweep-inl.h"
 #include "mirror/object-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 
@@ -413,11 +413,11 @@
     if (UNLIKELY(obj == nullptr || !IsAligned<kPageSize>(obj) ||
                  (kIsDebugBuild && large_object_space != nullptr &&
                      !large_object_space->Contains(obj)))) {
-      LOG(INTERNAL_FATAL) << "Tried to mark " << obj << " not contained by any spaces";
+      LOG(FATAL_WITHOUT_ABORT) << "Tried to mark " << obj << " not contained by any spaces";
       if (holder_ != nullptr) {
         size_t holder_size = holder_->SizeOf();
         ArtField* field = holder_->FindFieldByOffset(offset_);
-        LOG(INTERNAL_FATAL) << "Field info: "
+        LOG(FATAL_WITHOUT_ABORT) << "Field info: "
                             << " holder=" << holder_
                             << " holder is "
                             << (mark_sweep_->GetHeap()->IsLiveObjectLocked(holder_)
@@ -440,13 +440,13 @@
         // Print the memory content of the holder.
         for (size_t i = 0; i < holder_size / sizeof(uint32_t); ++i) {
           uint32_t* p = reinterpret_cast<uint32_t*>(holder_);
-          LOG(INTERNAL_FATAL) << &p[i] << ": " << "holder+" << (i * sizeof(uint32_t)) << " = "
+          LOG(FATAL_WITHOUT_ABORT) << &p[i] << ": " << "holder+" << (i * sizeof(uint32_t)) << " = "
                               << std::hex << p[i];
         }
       }
-      PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL);
-      MemMap::DumpMaps(LOG(INTERNAL_FATAL), true);
-      LOG(INTERNAL_FATAL) << "Attempting see if it's a bad thread root";
+      PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+      MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+      LOG(FATAL_WITHOUT_ABORT) << "Attempting see if it's a bad thread root";
       mark_sweep_->VerifySuspendedThreadRoots();
       LOG(FATAL) << "Can't mark invalid object";
     }
@@ -574,7 +574,7 @@
     if (heap->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
       space::LargeObjectSpace* large_object_space = heap->GetLargeObjectsSpace();
       if (large_object_space != nullptr && !large_object_space->Contains(root)) {
-        LOG(INTERNAL_FATAL) << "Found invalid root: " << root << " " << info;
+        LOG(FATAL_WITHOUT_ABORT) << "Found invalid root: " << root << " " << info;
       }
     }
   }
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 47e6ca3..2e97172 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -296,7 +296,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
     if (from_space_->HasAddress(ref)) {
-      Runtime::Current()->GetHeap()->DumpObject(LOG(INFO), obj);
+      Runtime::Current()->GetHeap()->DumpObject(LOG_STREAM(INFO), obj);
       LOG(FATAL) << ref << " found in from space";
     }
   }
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index b0ca18e..6d61c64 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -50,6 +50,7 @@
     CHECK_EQ(self->GetState(), kRunnable);
     self->AssertThreadSuspensionIsAllowable();
     self->AssertNoPendingException();
+    self->PoisonObjectPointers();
   }
   // Need to check that we arent the large object allocator since the large object allocation code
   // path this function. If we didn't check we would have an infinite loop.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index cb5226b..88e4624 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -61,6 +61,7 @@
 #include "intern_table.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "obj_ptr-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
@@ -69,7 +70,7 @@
 #include "reflection.h"
 #include "runtime.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "handle_scope-inl.h"
 #include "thread_list.h"
 #include "well_known_classes.h"
@@ -327,9 +328,9 @@
             continue;
           }
 
-          space::ImageSpace::CreateMultiImageLocations(image_file_name,
-                                                       boot_classpath,
-                                                       &image_file_names);
+          space::ImageSpace::ExtractMultiImageLocations(image_file_name,
+                                                        boot_classpath,
+                                                        &image_file_names);
         }
       } else {
         LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
@@ -630,7 +631,7 @@
     bool no_gap = MemMap::CheckNoGaps(first_space->GetMemMap(), non_moving_space_->GetMemMap());
     if (!no_gap) {
       PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
-      MemMap::DumpMaps(LOG(ERROR), true);
+      MemMap::DumpMaps(LOG_STREAM(ERROR), true);
       LOG(FATAL) << "There's a gap between the image space and the non-moving space";
     }
   }
@@ -1507,13 +1508,14 @@
       << static_cast<int>(100 * managed_utilization) << "%.";
 }
 
-bool Heap::IsValidObjectAddress(const mirror::Object* obj) const {
+bool Heap::IsValidObjectAddress(ObjPtr<mirror::Object> obj) const {
   // Note: we deliberately don't take the lock here, and mustn't test anything that would require
   // taking the lock.
   if (obj == nullptr) {
     return true;
   }
-  return IsAligned<kObjectAlignment>(obj) && FindSpaceFromObject(obj, true) != nullptr;
+  return IsAligned<kObjectAlignment>(obj.Decode()) &&
+      FindSpaceFromObject(obj.Decode(), true) != nullptr;
 }
 
 bool Heap::IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const {
@@ -3157,14 +3159,14 @@
     // Dump mod-union tables.
     for (const auto& table_pair : mod_union_tables_) {
       accounting::ModUnionTable* mod_union_table = table_pair.second;
-      mod_union_table->Dump(LOG(ERROR) << mod_union_table->GetName() << ": ");
+      mod_union_table->Dump(LOG_STREAM(ERROR) << mod_union_table->GetName() << ": ");
     }
     // Dump remembered sets.
     for (const auto& table_pair : remembered_sets_) {
       accounting::RememberedSet* remembered_set = table_pair.second;
-      remembered_set->Dump(LOG(ERROR) << remembered_set->GetName() << ": ");
+      remembered_set->Dump(LOG_STREAM(ERROR) << remembered_set->GetName() << ": ");
     }
-    DumpSpaces(LOG(ERROR));
+    DumpSpaces(LOG_STREAM(ERROR));
   }
   return visitor.GetFailureCount();
 }
@@ -3565,9 +3567,9 @@
   max_allowed_footprint_ = max_allowed_footprint;
 }
 
-bool Heap::IsMovableObject(const mirror::Object* obj) const {
+bool Heap::IsMovableObject(ObjPtr<mirror::Object> obj) const {
   if (kMovingCollector) {
-    space::Space* space = FindContinuousSpaceFromObject(obj, true);
+    space::Space* space = FindContinuousSpaceFromObject(obj.Decode(), true);
     if (space != nullptr) {
       // TODO: Check large object?
       return space->CanMoveObjects();
@@ -3727,7 +3729,7 @@
   args[0].l = arg.get();
   InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_FinalizerReference_add, args);
   // Restore object in case it gets moved.
-  *object = soa.Decode<mirror::Object*>(arg.get());
+  *object = soa.Decode<mirror::Object>(arg.get()).Decode();
 }
 
 void Heap::RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 10bebef..e32f057 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -34,6 +34,7 @@
 #include "gc/collector_type.h"
 #include "gc/space/large_object_space.h"
 #include "globals.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "process_state.h"
@@ -274,7 +275,7 @@
   // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock,
   // and doesn't abort on error, allowing the caller to report more
   // meaningful diagnostics.
-  bool IsValidObjectAddress(const mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsValidObjectAddress(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Faster alternative to IsHeapAddress since finding if an object is in the large object space is
   // very slow.
@@ -290,7 +291,7 @@
       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   // Returns true if there is any chance that the object (obj) will move.
-  bool IsMovableObject(const mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsMovableObject(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Enables us to compacting GC until objects are released.
   void IncrementDisableMovingGC(Thread* self) REQUIRES(!*gc_complete_lock_);
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index a3cefd9..515a6fd 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -22,7 +22,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index e172f85..9694597 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -24,7 +24,7 @@
 #include "reference_processor-inl.h"
 #include "reflection.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "task_processor.h"
 #include "utils.h"
 #include "well_known_classes.h"
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index 35bf718..5b8a3c2 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
+#include <sstream>
+
 #include "common_runtime_test.h"
 #include "reference_queue.h"
 #include "handle_scope-inl.h"
 #include "mirror/class-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace gc {
@@ -65,7 +67,9 @@
   StackHandleScope<20> hs(self);
   Mutex lock("Reference queue lock");
   ReferenceQueue queue(&lock);
-  queue.Dump(LOG(INFO));
+  std::ostringstream oss;
+  queue.Dump(oss);
+  LOG(INFO) << oss.str();
   auto weak_ref_class = hs.NewHandle(
       Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
                                                       ScopedNullHandle<mirror::ClassLoader>()));
@@ -78,10 +82,16 @@
   ASSERT_TRUE(ref1.Get() != nullptr);
   auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference()));
   ASSERT_TRUE(ref2.Get() != nullptr);
+
   queue.EnqueueReference(ref1.Get());
-  queue.Dump(LOG(INFO));
+  oss.str("");
+  queue.Dump(oss);
+  LOG(INFO) << oss.str();
+
   queue.EnqueueReference(ref2.Get());
-  queue.Dump(LOG(INFO));
+  oss.str("");
+  queue.Dump(oss);
+  LOG(INFO) << oss.str();
 }
 
 }  // namespace gc
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e41c532..a40e408 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -363,15 +363,29 @@
   return true;
 }
 
-static bool ImageCreationAllowed(bool is_global_cache, std::string* error_msg) {
+static bool CanWriteToDalvikCache(const InstructionSet isa) {
+  const std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(isa));
+  if (access(dalvik_cache.c_str(), O_RDWR) == 0) {
+    return true;
+  } else if (errno != EACCES) {
+    PLOG(WARNING) << "CanWriteToDalvikCache returned error other than EACCES";
+  }
+  return false;
+}
+
+static bool ImageCreationAllowed(bool is_global_cache,
+                                 const InstructionSet isa,
+                                 std::string* error_msg) {
   // Anyone can write into a "local" cache.
   if (!is_global_cache) {
     return true;
   }
 
-  // Only the zygote is allowed to create the global boot image.
+  // Only the zygote running as root is allowed to create the global boot image.
+  // If the zygote is running as non-root (and cannot write to the dalvik-cache),
+  // then image creation is not allowed..
   if (Runtime::Current()->IsZygote()) {
-    return true;
+    return CanWriteToDalvikCache(isa);
   }
 
   *error_msg = "Only the zygote can create the global boot image.";
@@ -714,7 +728,7 @@
 
     VLOG(image) << "ImageSpace::Init exiting " << *space.get();
     if (VLOG_IS_ON(image)) {
-      logger.Dump(LOG(INFO));
+      logger.Dump(LOG_STREAM(INFO));
     }
     return space;
   }
@@ -1284,7 +1298,7 @@
       }
     }
     if (VLOG_IS_ON(image)) {
-      logger.Dump(LOG(INFO));
+      logger.Dump(LOG_STREAM(INFO));
     }
     return true;
   }
@@ -1410,7 +1424,7 @@
 
   // Step 0.a: If we're the zygote, mark boot.
   const bool is_zygote = Runtime::Current()->IsZygote();
-  if (is_zygote && !secondary_image) {
+  if (is_zygote && !secondary_image && CanWriteToDalvikCache(image_isa)) {
     MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
   }
 
@@ -1525,7 +1539,7 @@
       local_error_msg = "Patching disabled.";
     } else if (secondary_image) {
       local_error_msg = "Cannot patch a secondary image.";
-    } else if (ImageCreationAllowed(is_global_cache, &local_error_msg)) {
+    } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
       bool patch_success =
           RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg);
       if (patch_success) {
@@ -1555,7 +1569,7 @@
       local_error_msg = "Image compilation disabled.";
     } else if (secondary_image) {
       local_error_msg = "Cannot compile a secondary image.";
-    } else if (ImageCreationAllowed(is_global_cache, &local_error_msg)) {
+    } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
       bool compilation_success = GenerateImage(cache_filename, image_isa, &local_error_msg);
       if (compilation_success) {
         std::unique_ptr<ImageSpace> compiled_space =
@@ -1619,9 +1633,54 @@
       << ",name=\"" << GetName() << "\"]";
 }
 
-void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_name,
-                                           const std::string& boot_classpath,
-                                           std::vector<std::string>* image_file_names) {
+std::string ImageSpace::GetMultiImageBootClassPath(
+    const std::vector<const char*>& dex_locations,
+    const std::vector<const char*>& oat_filenames,
+    const std::vector<const char*>& image_filenames) {
+  DCHECK_GT(oat_filenames.size(), 1u);
+  // If the image filename was adapted (e.g., for our tests), we need to change this here,
+  // too, but need to strip all path components (they will be re-established when loading).
+  std::ostringstream bootcp_oss;
+  bool first_bootcp = true;
+  for (size_t i = 0; i < dex_locations.size(); ++i) {
+    if (!first_bootcp) {
+      bootcp_oss << ":";
+    }
+
+    std::string dex_loc = dex_locations[i];
+    std::string image_filename = image_filenames[i];
+
+    // Use the dex_loc path, but the image_filename name (without path elements).
+    size_t dex_last_slash = dex_loc.rfind('/');
+
+    // npos is max(size_t). That makes this a bit ugly.
+    size_t image_last_slash = image_filename.rfind('/');
+    size_t image_last_at = image_filename.rfind('@');
+    size_t image_last_sep = (image_last_slash == std::string::npos)
+                                ? image_last_at
+                                : (image_last_at == std::string::npos)
+                                      ? std::string::npos
+                                      : std::max(image_last_slash, image_last_at);
+    // Note: whenever image_last_sep == npos, +1 overflow means using the full string.
+
+    if (dex_last_slash == std::string::npos) {
+      dex_loc = image_filename.substr(image_last_sep + 1);
+    } else {
+      dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
+          image_filename.substr(image_last_sep + 1);
+    }
+
+    // Image filenames already end with .art, no need to replace.
+
+    bootcp_oss << dex_loc;
+    first_bootcp = false;
+  }
+  return bootcp_oss.str();
+}
+
+void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
+                                            const std::string& boot_classpath,
+                                            std::vector<std::string>* image_file_names) {
   DCHECK(image_file_names != nullptr);
 
   std::vector<std::string> images;
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index c407259..0ba131b 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -125,10 +125,14 @@
 
   // Use the input image filename to adapt the names in the given boot classpath to establish
   // complete locations for secondary images.
-  static void CreateMultiImageLocations(const std::string& input_image_file_name,
+  static void ExtractMultiImageLocations(const std::string& input_image_file_name,
                                         const std::string& boot_classpath,
                                         std::vector<std::string>* image_filenames);
 
+  static std::string GetMultiImageBootClassPath(const std::vector<const char*>& dex_locations,
+                                                const std::vector<const char*>& oat_filenames,
+                                                const std::vector<const char*>& image_filenames);
+
   // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
   uint8_t* GetImageEnd() const {
     return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 010f677..0030326 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -27,7 +27,7 @@
 #include "base/stl_util.h"
 #include "image.h"
 #include "os.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "space-inl.h"
 #include "thread-inl.h"
 
@@ -192,7 +192,7 @@
   auto it = large_objects_.find(ptr);
   if (UNLIKELY(it == large_objects_.end())) {
     ScopedObjectAccess soa(self);
-    Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL));
+    Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(FATAL_WITHOUT_ABORT));
     LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
   }
   MemMap* mem_map = it->second.mem_map;
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
index ad38724..2544914 100644
--- a/runtime/gc/space/large_object_space_test.cc
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -98,7 +98,9 @@
       }
     }
     // Test that dump doesn't crash.
-    los->Dump(LOG(INFO));
+    std::ostringstream oss;
+    los->Dump(oss);
+    LOG(INFO) << oss.str();
 
     size_t bytes_allocated = 0, bytes_tl_bulk_allocated;
     // Checks that the coalescing works.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index e24741a..23cae7c 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -38,7 +38,7 @@
   if (mem_map.get() == nullptr) {
     LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
         << PrettySize(capacity) << " with message " << error_msg;
-    MemMap::DumpMaps(LOG(ERROR));
+    MemMap::DumpMaps(LOG_STREAM(ERROR));
     return nullptr;
   }
   return new RegionSpace(name, mem_map.release());
diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc
index 170f927..7bc4dc4 100644
--- a/runtime/gc/space/space_create_test.cc
+++ b/runtime/gc/space/space_create_test.cc
@@ -18,7 +18,7 @@
 
 #include "dlmalloc_space.h"
 #include "rosalloc_space.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index bd600fe..17d7c87 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -26,7 +26,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 #include "zygote_space.h"
 
diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc
index 7c1ec8a..af8a444 100644
--- a/runtime/gc/system_weak_test.cc
+++ b/runtime/gc/system_weak_test.cc
@@ -26,7 +26,7 @@
 #include "handle_scope-inl.h"
 #include "heap.h"
 #include "mirror/string.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 
 namespace art {
diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc
index a49121b..0704a68 100644
--- a/runtime/gc/task_processor.cc
+++ b/runtime/gc/task_processor.cc
@@ -17,7 +17,7 @@
 #include "task_processor.h"
 
 #include "base/time_utils.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace gc {
diff --git a/runtime/handle.h b/runtime/handle.h
index d4c13d4..c41010a 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -23,6 +23,7 @@
 #include "base/mutex.h"
 #include "base/value_object.h"
 #include "jni.h"
+#include "obj_ptr.h"
 #include "stack_reference.h"
 
 namespace art {
@@ -130,6 +131,14 @@
     return old;
   }
 
+  ALWAYS_INLINE T* Assign(ObjPtr<T> reference) REQUIRES_SHARED(Locks::mutator_lock_) {
+    StackReference<mirror::Object>* ref = Handle<T>::GetReference();
+    T* old = down_cast<T*>(ref->AsMirrorPtr());
+    ref->Assign(reference.Decode());
+    return old;
+  }
+
+
   template<typename S>
   explicit MutableHandle(const MutableHandle<S>& handle) REQUIRES_SHARED(Locks::mutator_lock_)
       : Handle<T>(handle) {
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 2e1b8ed..75a0391 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -21,6 +21,7 @@
 
 #include "base/mutex.h"
 #include "handle.h"
+#include "obj_ptr-inl.h"
 #include "thread-inl.h"
 #include "verify_object-inl.h"
 
@@ -107,12 +108,21 @@
   return h;
 }
 
+template<size_t kNumReferences> template<class MirrorType, bool kPoison>
+inline MutableHandle<MirrorType> StackHandleScope<kNumReferences>::NewHandle(
+    ObjPtr<MirrorType, kPoison> object) {
+  return NewHandle(object.Decode());
+}
+
 template<size_t kNumReferences> template<class T>
 inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
-  SetReference(pos_, *object);
-  MutableHandle<T> h(GetHandle<T>(pos_));
-  pos_++;
-  return HandleWrapper<T>(object, h);
+  return HandleWrapper<T>(object, NewHandle(*object));
+}
+
+template<size_t kNumReferences> template<class T>
+inline HandleWrapperObjPtr<T> StackHandleScope<kNumReferences>::NewHandleWrapper(
+    ObjPtr<T>* object) {
+  return HandleWrapperObjPtr<T>(object, NewHandle(*object));
 }
 
 template<size_t kNumReferences>
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 37eed99..2b283ae 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -28,6 +28,9 @@
 #include "verify_object.h"
 
 namespace art {
+
+template<class MirrorType, bool kPoison> class ObjPtr;
+
 namespace mirror {
 class Object;
 }
@@ -125,7 +128,7 @@
 };
 
 // A wrapper which wraps around Object** and restores the pointer in the destructor.
-// TODO: Add more functionality.
+// TODO: Delete
 template<class T>
 class HandleWrapper : public MutableHandle<T> {
  public:
@@ -143,6 +146,26 @@
   T** const obj_;
 };
 
+
+// A wrapper which wraps around ObjPtr<Object>* and restores the pointer in the destructor.
+// TODO: Add more functionality.
+template<class T>
+class HandleWrapperObjPtr : public MutableHandle<T> {
+ public:
+  HandleWrapperObjPtr(ObjPtr<T>* obj, const MutableHandle<T>& handle)
+      : MutableHandle<T>(handle), obj_(obj) {}
+
+  HandleWrapperObjPtr(const HandleWrapperObjPtr&) = default;
+
+  ~HandleWrapperObjPtr() {
+    *obj_ = ObjPtr<T>(MutableHandle<T>::Get());
+  }
+
+ private:
+  ObjPtr<T>* const obj_;
+};
+
+
 // Scoped handle storage of a fixed size that is usually stack allocated.
 template<size_t kNumReferences>
 class PACKED(4) StackHandleScope FINAL : public HandleScope {
@@ -157,6 +180,14 @@
   ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<class T>
+  ALWAYS_INLINE HandleWrapperObjPtr<T> NewHandleWrapper(ObjPtr<T>* object)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  template<class MirrorType, bool kPoison>
+  ALWAYS_INLINE MutableHandle<MirrorType> NewHandle(ObjPtr<MirrorType, kPoison> object)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
   ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index 58f3800..c269a37 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -17,7 +17,7 @@
 #include "base/enums.h"
 #include "gtest/gtest.h"
 #include "handle_scope-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 921dde1..ecb2157 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -59,7 +59,7 @@
 #include "mirror/object-inl.h"
 #include "os.h"
 #include "safe_map.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 
 namespace art {
diff --git a/runtime/image-inl.h b/runtime/image-inl.h
index 669649e..da18ae5 100644
--- a/runtime/image-inl.h
+++ b/runtime/image-inl.h
@@ -20,6 +20,7 @@
 #include "image.h"
 
 #include "art_method.h"
+#include "imt_conflict_table.h"
 #include "imtable.h"
 #include "read_barrier-inl.h"
 
diff --git a/runtime/image.cc b/runtime/image.cc
index 7e6790a..299d5fd 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '0', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '1', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/imt_conflict_table.h b/runtime/imt_conflict_table.h
new file mode 100644
index 0000000..fdd10fe
--- /dev/null
+++ b/runtime/imt_conflict_table.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_IMT_CONFLICT_TABLE_H_
+#define ART_RUNTIME_IMT_CONFLICT_TABLE_H_
+
+#include <cstddef>
+
+#include "base/casts.h"
+#include "base/enums.h"
+#include "base/macros.h"
+
+namespace art {
+
+class ArtMethod;
+
+// Table to resolve IMT conflicts at runtime. The table is attached to
+// the jni entrypoint of IMT conflict ArtMethods.
+// The table contains a list of pairs of { interface_method, implementation_method }
+// with the last entry being null to make an assembly implementation of a lookup
+// faster.
+class ImtConflictTable {
+  enum MethodIndex {
+    kMethodInterface,
+    kMethodImplementation,
+    kMethodCount,  // Number of elements in enum.
+  };
+
+ public:
+  // Build a new table copying `other` and adding the new entry formed of
+  // the pair { `interface_method`, `implementation_method` }
+  ImtConflictTable(ImtConflictTable* other,
+                   ArtMethod* interface_method,
+                   ArtMethod* implementation_method,
+                   PointerSize pointer_size) {
+    const size_t count = other->NumEntries(pointer_size);
+    for (size_t i = 0; i < count; ++i) {
+      SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
+      SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
+    }
+    SetInterfaceMethod(count, pointer_size, interface_method);
+    SetImplementationMethod(count, pointer_size, implementation_method);
+    // Add the null marker.
+    SetInterfaceMethod(count + 1, pointer_size, nullptr);
+    SetImplementationMethod(count + 1, pointer_size, nullptr);
+  }
+
+  // num_entries excludes the header.
+  ImtConflictTable(size_t num_entries, PointerSize pointer_size) {
+    SetInterfaceMethod(num_entries, pointer_size, nullptr);
+    SetImplementationMethod(num_entries, pointer_size, nullptr);
+  }
+
+  // Set an entry at an index.
+  void SetInterfaceMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
+    SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
+  }
+
+  void SetImplementationMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
+    SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
+  }
+
+  ArtMethod* GetInterfaceMethod(size_t index, PointerSize pointer_size) const {
+    return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
+  }
+
+  ArtMethod* GetImplementationMethod(size_t index, PointerSize pointer_size) const {
+    return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
+  }
+
+  // Return true if two conflict tables are the same.
+  bool Equals(ImtConflictTable* other, PointerSize pointer_size) const {
+    size_t num = NumEntries(pointer_size);
+    if (num != other->NumEntries(pointer_size)) {
+      return false;
+    }
+    for (size_t i = 0; i < num; ++i) {
+      if (GetInterfaceMethod(i, pointer_size) != other->GetInterfaceMethod(i, pointer_size) ||
+          GetImplementationMethod(i, pointer_size) !=
+              other->GetImplementationMethod(i, pointer_size)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // Visit all of the entries.
+  // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
+  // and also returns one. The order is <interface, implementation>.
+  template<typename Visitor>
+  void Visit(const Visitor& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS {
+    uint32_t table_index = 0;
+    for (;;) {
+      ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
+      if (interface_method == nullptr) {
+        break;
+      }
+      ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
+      auto input = std::make_pair(interface_method, implementation_method);
+      std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
+      if (input.first != updated.first) {
+        SetInterfaceMethod(table_index, pointer_size, updated.first);
+      }
+      if (input.second != updated.second) {
+        SetImplementationMethod(table_index, pointer_size, updated.second);
+      }
+      ++table_index;
+    }
+  }
+
+  // Lookup the implementation ArtMethod associated to `interface_method`. Return null
+  // if not found.
+  ArtMethod* Lookup(ArtMethod* interface_method, PointerSize pointer_size) const {
+    uint32_t table_index = 0;
+    for (;;) {
+      ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
+      if (current_interface_method == nullptr) {
+        break;
+      }
+      if (current_interface_method == interface_method) {
+        return GetImplementationMethod(table_index, pointer_size);
+      }
+      ++table_index;
+    }
+    return nullptr;
+  }
+
+  // Compute the number of entries in this table.
+  size_t NumEntries(PointerSize pointer_size) const {
+    uint32_t table_index = 0;
+    while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
+      ++table_index;
+    }
+    return table_index;
+  }
+
+  // Compute the size in bytes taken by this table.
+  size_t ComputeSize(PointerSize pointer_size) const {
+    // Add the end marker.
+    return ComputeSize(NumEntries(pointer_size), pointer_size);
+  }
+
+  // Compute the size in bytes needed for copying the given `table` and add
+  // one more entry.
+  static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, PointerSize pointer_size) {
+    return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
+  }
+
+  // Compute size with a fixed number of entries.
+  static size_t ComputeSize(size_t num_entries, PointerSize pointer_size) {
+    return (num_entries + 1) * EntrySize(pointer_size);  // Add one for null terminator.
+  }
+
+  static size_t EntrySize(PointerSize pointer_size) {
+    return static_cast<size_t>(pointer_size) * static_cast<size_t>(kMethodCount);
+  }
+
+ private:
+  ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const {
+    if (pointer_size == PointerSize::k64) {
+      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index]));
+    } else {
+      return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index]));
+    }
+  }
+
+  void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
+    if (pointer_size == PointerSize::k64) {
+      data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method));
+    } else {
+      data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method));
+    }
+  }
+
+  // Array of entries that the assembly stubs will iterate over. Note that this is
+  // not fixed size, and we allocate data prior to calling the constructor
+  // of ImtConflictTable.
+  union {
+    uint32_t data32_[0];
+    uint64_t data64_[0];
+  };
+
+  DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_IMT_CONFLICT_TABLE_H_
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.h b/runtime/imtable-inl.h
similarity index 61%
copy from runtime/native/java_lang_reflect_AbstractMethod.h
copy to runtime/imtable-inl.h
index 222e5a0..0cb9b5e 100644
--- a/runtime/native/java_lang_reflect_AbstractMethod.h
+++ b/runtime/imtable-inl.h
@@ -14,15 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#ifndef ART_RUNTIME_IMTABLE_INL_H_
+#define ART_RUNTIME_IMTABLE_INL_H_
 
-#include <jni.h>
+#include "imtable.h"
+
+#include "art_method-inl.h"
 
 namespace art {
 
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env);
+inline uint32_t ImTable::GetBaseImtHash(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+  return method->GetDexMethodIndex();
+}
+
+inline uint32_t ImTable::GetImtIndex(ArtMethod* method) {
+  return GetBaseImtHash(method) % ImTable::kSize;
+}
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#endif  // ART_RUNTIME_IMTABLE_INL_H_
+
diff --git a/runtime/imtable.h b/runtime/imtable.h
index 2416621..6df890d 100644
--- a/runtime/imtable.h
+++ b/runtime/imtable.h
@@ -21,9 +21,13 @@
 #error IMT_SIZE not defined
 #endif
 
+#include "base/enums.h"
+#include "base/macros.h"
+
 namespace art {
 
 class ArtMethod;
+class DexFile;
 
 class ImTable {
  public:
@@ -69,6 +73,19 @@
   constexpr static size_t SizeInBytes(PointerSize pointer_size) {
     return kSize * static_cast<size_t>(pointer_size);
   }
+
+  // Converts a method to the base hash used in GetImtIndex.
+  ALWAYS_INLINE static inline uint32_t GetBaseImtHash(ArtMethod* method)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  ALWAYS_INLINE static inline uint32_t GetBaseImtHash(const DexFile* dex_file, uint32_t method_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // The (complete) hashing scheme to map an ArtMethod to a slot in the Interface Method Table
+  // (IMT).
+  ALWAYS_INLINE static inline uint32_t GetImtIndex(ArtMethod* method)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  ALWAYS_INLINE static inline uint32_t GetImtIndex(const DexFile* dex_file, uint32_t method_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 4f81b59..202e472 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -21,7 +21,7 @@
 #include "nth_caller_visitor.h"
 #include "reference_table.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "utils.h"
 #include "verify_object-inl.h"
@@ -188,7 +188,7 @@
         ScopedObjectAccess soa(self);
         LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
         if (kDumpStackOnNonLocalReference) {
-          self->Dump(LOG(WARNING));
+          self->Dump(LOG_STREAM(WARNING));
         }
       }
       return true;
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 61bcadd..58d487d 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -19,7 +19,7 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index abe3184..7f9f04f 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -25,7 +25,7 @@
 #include "handle_scope-inl.h"
 #include "jvalue.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 #include "thread-inl.h"
 
@@ -458,7 +458,7 @@
   instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
   ClassLinker* class_linker = runtime->GetClassLinker();
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
   mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
   ASSERT_TRUE(klass != nullptr);
   ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V",
@@ -505,7 +505,7 @@
   instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
   ClassLinker* class_linker = runtime->GetClassLinker();
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
   mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
   ASSERT_TRUE(klass != nullptr);
   ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V",
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 620e15b..74cec57 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -20,7 +20,7 @@
 #include "mirror/object.h"
 #include "handle_scope-inl.h"
 #include "mirror/string.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 0003e72..c270df7 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -23,7 +23,7 @@
 #include "interpreter_mterp_impl.h"
 #include "interpreter_switch_impl.h"
 #include "mirror/string-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "stack.h"
 #include "unstarted_runtime.h"
@@ -51,7 +51,7 @@
         ScopedThreadStateChange tsc(self, kNative);
         jresult = fn(soa.Env(), klass.get());
       }
-      result->SetL(soa.Decode<Object*>(jresult));
+      result->SetL(soa.Decode<Object>(jresult).Decode());
     } else if (shorty == "V") {
       typedef void (fntype)(JNIEnv*, jclass);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -93,7 +93,7 @@
         ScopedThreadStateChange tsc(self, kNative);
         jresult = fn(soa.Env(), klass.get(), arg0.get());
       }
-      result->SetL(soa.Decode<Object*>(jresult));
+      result->SetL(soa.Decode<Object>(jresult).Decode());
     } else if (shorty == "IIZ") {
       typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -191,7 +191,7 @@
         ScopedThreadStateChange tsc(self, kNative);
         jresult = fn(soa.Env(), rcvr.get());
       }
-      result->SetL(soa.Decode<Object*>(jresult));
+      result->SetL(soa.Decode<Object>(jresult).Decode());
     } else if (shorty == "V") {
       typedef void (fntype)(JNIEnv*, jobject);
       fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -212,7 +212,7 @@
         ScopedThreadStateChange tsc(self, kNative);
         jresult = fn(soa.Env(), rcvr.get(), arg0.get());
       }
-      result->SetL(soa.Decode<Object*>(jresult));
+      result->SetL(soa.Decode<Object>(jresult).Decode());
       ScopedThreadStateChange tsc(self, kNative);
     } else if (shorty == "III") {
       typedef jint (fntype)(JNIEnv*, jobject, jint, jint);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 77c3f0f..5934f13 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -576,9 +576,7 @@
   // Replace calls to String.<init> with equivalent StringFactory call.
   if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass()
                && called_method->IsConstructor())) {
-    ScopedObjectAccessUnchecked soa(self);
-    jmethodID mid = soa.EncodeMethod(called_method);
-    called_method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    called_method = WellKnownClasses::StringInitToStringFactory(called_method);
     string_init = true;
   }
 
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 814adf7..0feb013 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -254,9 +254,9 @@
   DCHECK_LT(string_idx % mirror::DexCache::kDexCacheStringCacheSize,
             declaring_class->GetDexFile().NumStringIds());
   mirror::String* string_ptr =
-      mirror::StringDexCachePair::LookupString(declaring_class->GetDexCacheStrings(),
-                                               string_idx,
-                                               mirror::DexCache::kDexCacheStringCacheSize).Read();
+      mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+                                         string_idx,
+                                         mirror::DexCache::kDexCacheStringCacheSize).Read();
   if (UNLIKELY(string_ptr == nullptr)) {
     StackHandleScope<1> hs(self);
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index a8c7d15..cf8d4bd 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -38,8 +38,8 @@
   int interp_size = (uintptr_t) artMterpAsmInstructionEnd -
                     (uintptr_t) artMterpAsmInstructionStart;
   if ((interp_size == 0) || (interp_size != (art::kNumPackedOpcodes * width))) {
-      LOG(art::FATAL) << "ERROR: unexpected asm interp size " << interp_size
-                      << "(did an instruction handler exceed " << width << " bytes?)";
+      LOG(FATAL) << "ERROR: unexpected asm interp size " << interp_size
+                 << "(did an instruction handler exceed " << width << " bytes?)";
   }
 }
 
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index d505aea..39846da 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -858,7 +858,7 @@
       ArtMethod* init_method = h_real_to_string_class->FindDirectMethod(
           "<init>", "()V", cl->GetImagePointerSize());
       if (init_method == nullptr) {
-        h_real_to_string_class->DumpClass(LOG(FATAL), mirror::Class::kDumpClassFullDetail);
+        h_real_to_string_class->DumpClass(LOG_STREAM(FATAL), mirror::Class::kDumpClassFullDetail);
       } else {
         JValue invoke_result;
         EnterInterpreterFromInvoke(self, init_method, h_real_to_string_obj.Get(), nullptr,
@@ -1619,9 +1619,9 @@
     uint32_t* args ATTRIBUTE_UNUSED, JValue* result) {
   ScopedObjectAccessUnchecked soa(self);
   if (Runtime::Current()->IsActiveTransaction()) {
-    result->SetL(soa.Decode<mirror::Object*>(self->CreateInternalStackTrace<true>(soa)));
+    result->SetL(soa.Decode<mirror::Object>(self->CreateInternalStackTrace<true>(soa)).Decode());
   } else {
-    result->SetL(soa.Decode<mirror::Object*>(self->CreateInternalStackTrace<false>(soa)));
+    result->SetL(soa.Decode<mirror::Object>(self->CreateInternalStackTrace<false>(soa)).Decode());
   }
 }
 
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index ba751ec..6a4add3 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -31,7 +31,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/string-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "transaction.h"
 
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 979495a..0c752ef 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -36,7 +36,7 @@
 #include "runtime-inl.h"
 #include "runtime_options.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 
@@ -532,17 +532,17 @@
   return true;
 }
 
-jobject JavaVMExt::AddGlobalRef(Thread* self, mirror::Object* obj) {
+jobject JavaVMExt::AddGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) {
   // Check for null after decoding the object to handle cleared weak globals.
   if (obj == nullptr) {
     return nullptr;
   }
   WriterMutexLock mu(self, globals_lock_);
-  IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj);
+  IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj.Decode());
   return reinterpret_cast<jobject>(ref);
 }
 
-jweak JavaVMExt::AddWeakGlobalRef(Thread* self, mirror::Object* obj) {
+jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) {
   if (obj == nullptr) {
     return nullptr;
   }
@@ -550,7 +550,7 @@
   while (UNLIKELY(!MayAccessWeakGlobals(self))) {
     weak_globals_add_condition_.WaitHoldingLocks(self);
   }
-  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
+  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj.Decode());
   return reinterpret_cast<jweak>(ref);
 }
 
@@ -755,15 +755,15 @@
     ScopedObjectAccess soa(env);
     // As the incoming class loader is reachable/alive during the call of this function,
     // it's okay to decode it without worrying about unexpectedly marking it alive.
-    mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(class_loader);
+    ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
 
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    if (class_linker->IsBootClassLoader(soa, loader)) {
+    if (class_linker->IsBootClassLoader(soa, loader.Decode())) {
       loader = nullptr;
       class_loader = nullptr;
     }
 
-    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
+    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Decode());
     CHECK(class_loader_allocator != nullptr);
   }
   if (library != nullptr) {
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index a10a72f..558ffff 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -22,6 +22,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "indirect_reference_table.h"
+#include "obj_ptr.h"
 #include "reference_table.h"
 
 namespace art {
@@ -123,10 +124,10 @@
   void BroadcastForNewWeakGlobals() REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!weak_globals_lock_);
 
-  jobject AddGlobalRef(Thread* self, mirror::Object* obj)
+  jobject AddGlobalRef(Thread* self, ObjPtr<mirror::Object> obj)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!globals_lock_);
 
-  jweak AddWeakGlobalRef(Thread* self, mirror::Object* obj)
+  jweak AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj)
     REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!weak_globals_lock_);
 
   void DeleteGlobalRef(Thread* self, jobject obj) REQUIRES(!globals_lock_);
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e2d29fe..6aebe9f 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -30,7 +30,7 @@
 #include "jdwp/jdwp_expand_buf.h"
 #include "jdwp/jdwp_priv.h"
 #include "jdwp/object_registry.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 #include "handle_scope-inl.h"
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index f6008ac..0f2d188 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -31,7 +31,7 @@
 #include "jdwp/jdwp_expand_buf.h"
 #include "jdwp/jdwp_priv.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
 
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index dbf04fe..e3bf3e5 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -25,7 +25,7 @@
 #include "base/time_utils.h"
 #include "debugger.h"
 #include "jdwp/jdwp_priv.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 2507fe9..3be7fd6 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -121,7 +121,7 @@
 
   netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (netState->listenSock < 0) {
-    PLOG(probe ? ERROR : FATAL) << "Socket create failed";
+    PLOG(probe ? ::android::base::ERROR : ::android::base::FATAL) << "Socket create failed";
     goto fail;
   }
 
@@ -129,7 +129,8 @@
   {
     int one = 1;
     if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
-      PLOG(probe ? ERROR : FATAL) << "setsockopt(SO_REUSEADDR) failed";
+      PLOG(probe ? ::android::base::ERROR : ::android::base::FATAL)
+          << "setsockopt(SO_REUSEADDR) failed";
       goto fail;
     }
   }
@@ -143,14 +144,15 @@
   inet_aton("127.0.0.1", &addr.addrInet.sin_addr);
 
   if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) {
-    PLOG(probe ? ERROR : FATAL) << "Attempt to bind to port " << port << " failed";
+    PLOG(probe ? ::android::base::ERROR : ::android::base::FATAL)
+        << "Attempt to bind to port " << port << " failed";
     goto fail;
   }
 
   netState->listenPort = port;
 
   if (listen(netState->listenSock, 5) != 0) {
-    PLOG(probe ? ERROR : FATAL) << "Listen failed";
+    PLOG(probe ? ::android::base::ERROR : ::android::base::FATAL) << "Listen failed";
     goto fail;
   }
 
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 5989b61..9ba62c9 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -19,7 +19,7 @@
 #include "handle_scope-inl.h"
 #include "jni_internal.h"
 #include "mirror/class.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index d984f45..afa52ca 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -337,7 +337,7 @@
 Jit::~Jit() {
   DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
   if (dump_info_on_shutdown_) {
-    DumpInfo(LOG(INFO));
+    DumpInfo(LOG_STREAM(INFO));
   }
   DeleteThreadPool();
   if (jit_compiler_handle_ != nullptr) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index c9227b1..2c6b249 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -32,7 +32,7 @@
 #include "linear_alloc.h"
 #include "mem_map.h"
 #include "oat_file-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 
 namespace art {
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index c8f4d94..764458a 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -26,7 +26,7 @@
 #include "mirror/class_loader.h"
 #include "handle_scope-inl.h"
 #include "jit/offline_profiling_info.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 42916c3..d23821b 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -26,7 +26,7 @@
 #include "base/time_utils.h"
 #include "compiler_filter.h"
 #include "oat_file_manager.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 
 namespace art {
@@ -447,7 +447,7 @@
     }
     instance_->shutting_down_ = true;
     if (dump_info) {
-      instance_->DumpInfo(LOG(INFO));
+      instance_->DumpInfo(LOG_STREAM(INFO));
     }
   }
 
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 216df2f..6ba187e 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -20,7 +20,7 @@
 #include "dex_instruction.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h
index dc6a3e8..685b056 100644
--- a/runtime/jni_env_ext-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -32,9 +32,10 @@
     if (check_jni) {
       size_t entry_count = locals.Capacity();
       if (entry_count > 16) {
-        locals.Dump(LOG(WARNING) << "Warning: more than 16 JNI local references: "
-            << entry_count << " (most recent was a " << PrettyTypeOf(obj) << ")\n");
-        // TODO: LOG(FATAL) in a later release?
+        locals.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: "
+                                        << entry_count << " (most recent was a "
+                                        << PrettyTypeOf(obj) << ")\n");
+      // TODO: LOG(FATAL) in a later release?
       }
     }
   }
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index a434442..7b27578 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -52,7 +52,7 @@
 #include "reflection.h"
 #include "runtime.h"
 #include "safe_map.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "thread.h"
 #include "utf.h"
@@ -100,14 +100,15 @@
 static void ReportInvalidJNINativeMethod(const ScopedObjectAccess& soa, mirror::Class* c,
                                          const char* kind, jint idx, bool return_errors)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  LOG(return_errors ? ERROR : FATAL) << "Failed to register native method in "
-      << PrettyDescriptor(c) << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8()
+  LOG(return_errors ? ::android::base::ERROR : ::android::base::FATAL)
+      << "Failed to register native method in " << PrettyDescriptor(c)
+      << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8()
       << ": " << kind << " is null at index " << idx;
   soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
                                  "%s is null at index %d", kind, idx);
 }
 
-static mirror::Class* EnsureInitialized(Thread* self, mirror::Class* klass)
+static ObjPtr<mirror::Class> EnsureInitialized(Thread* self, ObjPtr<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (LIKELY(klass->IsInitialized())) {
     return klass;
@@ -123,7 +124,7 @@
 static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
                               const char* name, const char* sig, bool is_static)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(jni_class));
+  ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class));
   if (c == nullptr) {
     return nullptr;
   }
@@ -142,31 +143,31 @@
     }
   }
   if (method == nullptr || method->IsStatic() != is_static) {
-    ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
+    ThrowNoSuchMethodError(soa, c.Decode(), name, sig, is_static ? "static" : "non-static");
     return nullptr;
   }
   return soa.EncodeMethod(method);
 }
 
-static mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa)
+static ObjPtr<mirror::ClassLoader> GetClassLoader(const ScopedObjectAccess& soa)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
   // If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
   if (method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
-    return soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
+    return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
   }
   // If we have a method, use its ClassLoader for context.
   if (method != nullptr) {
     return method->GetDeclaringClass()->GetClassLoader();
   }
   // We don't have a method, so try to use the system ClassLoader.
-  mirror::ClassLoader* class_loader =
-      soa.Decode<mirror::ClassLoader*>(Runtime::Current()->GetSystemClassLoader());
+  ObjPtr<mirror::ClassLoader> class_loader =
+      soa.Decode<mirror::ClassLoader>(Runtime::Current()->GetSystemClassLoader());
   if (class_loader != nullptr) {
     return class_loader;
   }
   // See if the override ClassLoader is set for gtests.
-  class_loader = soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
+  class_loader = soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
   if (class_loader != nullptr) {
     // If so, CommonCompilerTest should have marked the runtime as a compiler not compiling an
     // image.
@@ -183,7 +184,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> c(
-      hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(jni_class))));
+      hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class))));
   if (c.Get() == nullptr) {
     return nullptr;
   }
@@ -271,7 +272,7 @@
   if (mid == nullptr) {
     ScopedObjectAccess soa(env);
     LOG(ERROR) << "No <init>" << signature << " in "
-        << PrettyClass(soa.Decode<mirror::Class*>(exception_class));
+        << PrettyClass(soa.Decode<mirror::Class>(exception_class));
     return JNI_ERR;
   }
 
@@ -281,7 +282,7 @@
     return JNI_ERR;
   }
   ScopedObjectAccess soa(env);
-  soa.Self()->SetException(soa.Decode<mirror::Throwable*>(exception.get()));
+  soa.Self()->SetException(soa.Decode<mirror::Throwable>(exception.get()).Decode());
   return JNI_OK;
 }
 
@@ -362,12 +363,12 @@
   static jfieldID FromReflectedField(JNIEnv* env, jobject jlr_field) {
     CHECK_NON_NULL_ARGUMENT(jlr_field);
     ScopedObjectAccess soa(env);
-    mirror::Object* obj_field = soa.Decode<mirror::Object*>(jlr_field);
+    ObjPtr<mirror::Object> obj_field = soa.Decode<mirror::Object>(jlr_field);
     if (obj_field->GetClass() != mirror::Field::StaticClass()) {
       // Not even a java.lang.reflect.Field, return null. TODO, is this check necessary?
       return nullptr;
     }
-    auto* field = static_cast<mirror::Field*>(obj_field);
+    ObjPtr<mirror::Field> field = down_cast<mirror::Field*>(obj_field.Decode());
     return soa.EncodeField(field->GetArtField());
   }
 
@@ -375,7 +376,7 @@
     CHECK_NON_NULL_ARGUMENT(mid);
     ScopedObjectAccess soa(env);
     ArtMethod* m = soa.DecodeMethod(mid);
-    mirror::AbstractMethod* method;
+    mirror::Executable* method;
     DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
     DCHECK(!Runtime::Current()->IsActiveTransaction());
     if (m->IsConstructor()) {
@@ -397,14 +398,14 @@
   static jclass GetObjectClass(JNIEnv* env, jobject java_object) {
     CHECK_NON_NULL_ARGUMENT(java_object);
     ScopedObjectAccess soa(env);
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     return soa.AddLocalReference<jclass>(o->GetClass());
   }
 
   static jclass GetSuperclass(JNIEnv* env, jclass java_class) {
     CHECK_NON_NULL_ARGUMENT(java_class);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
     return soa.AddLocalReference<jclass>(c->IsInterface() ? nullptr : c->GetSuperClass());
   }
 
@@ -414,9 +415,9 @@
     CHECK_NON_NULL_ARGUMENT_RETURN(java_class1, JNI_FALSE);
     CHECK_NON_NULL_ARGUMENT_RETURN(java_class2, JNI_FALSE);
     ScopedObjectAccess soa(env);
-    mirror::Class* c1 = soa.Decode<mirror::Class*>(java_class1);
-    mirror::Class* c2 = soa.Decode<mirror::Class*>(java_class2);
-    return c2->IsAssignableFrom(c1) ? JNI_TRUE : JNI_FALSE;
+    ObjPtr<mirror::Class> c1 = soa.Decode<mirror::Class>(java_class1);
+    ObjPtr<mirror::Class> c2 = soa.Decode<mirror::Class>(java_class2);
+    return c2->IsAssignableFrom(c1.Decode()) ? JNI_TRUE : JNI_FALSE;
   }
 
   static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass java_class) {
@@ -426,19 +427,19 @@
       return JNI_TRUE;
     } else {
       ScopedObjectAccess soa(env);
-      mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
-      mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+      ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
+      ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
       return obj->InstanceOf(c) ? JNI_TRUE : JNI_FALSE;
     }
   }
 
   static jint Throw(JNIEnv* env, jthrowable java_exception) {
     ScopedObjectAccess soa(env);
-    mirror::Throwable* exception = soa.Decode<mirror::Throwable*>(java_exception);
+    ObjPtr<mirror::Throwable> exception = soa.Decode<mirror::Throwable>(java_exception);
     if (exception == nullptr) {
       return JNI_ERR;
     }
-    soa.Self()->SetException(exception);
+    soa.Self()->SetException(exception.Decode());
     return JNI_OK;
   }
 
@@ -508,7 +509,7 @@
 
   static jobject PopLocalFrame(JNIEnv* env, jobject java_survivor) {
     ScopedObjectAccess soa(env);
-    mirror::Object* survivor = soa.Decode<mirror::Object*>(java_survivor);
+    ObjPtr<mirror::Object> survivor = soa.Decode<mirror::Object>(java_survivor);
     soa.Env()->PopFrame();
     return soa.AddLocalReference<jobject>(survivor);
   }
@@ -521,8 +522,8 @@
 
   static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
     ScopedObjectAccess soa(env);
-    mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
-    return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj);
+    ObjPtr<mirror::Object> decoded_obj = soa.Decode<mirror::Object>(obj);
+    return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj.Decode());
   }
 
   static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
@@ -533,8 +534,8 @@
 
   static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
     ScopedObjectAccess soa(env);
-    mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
-    return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj);
+    ObjPtr<mirror::Object> decoded_obj = soa.Decode<mirror::Object>(obj);
+    return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj.Decode());
   }
 
   static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
@@ -545,7 +546,7 @@
 
   static jobject NewLocalRef(JNIEnv* env, jobject obj) {
     ScopedObjectAccess soa(env);
-    mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
+    ObjPtr<mirror::Object> decoded_obj = soa.Decode<mirror::Object>(obj);
     // Check for null after decoding the object to handle cleared weak globals.
     if (decoded_obj == nullptr) {
       return nullptr;
@@ -578,7 +579,7 @@
       return JNI_TRUE;
     } else {
       ScopedObjectAccess soa(env);
-      return (soa.Decode<mirror::Object*>(obj1) == soa.Decode<mirror::Object*>(obj2))
+      return (soa.Decode<mirror::Object>(obj1) == soa.Decode<mirror::Object>(obj2))
               ? JNI_TRUE : JNI_FALSE;
     }
   }
@@ -586,7 +587,7 @@
   static jobject AllocObject(JNIEnv* env, jclass java_class) {
     CHECK_NON_NULL_ARGUMENT(java_class);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
+    ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(java_class));
     if (c == nullptr) {
       return nullptr;
     }
@@ -612,13 +613,15 @@
     CHECK_NON_NULL_ARGUMENT(java_class);
     CHECK_NON_NULL_ARGUMENT(mid);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
+    ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(),
+                                                soa.Decode<mirror::Class>(java_class));
     if (c == nullptr) {
       return nullptr;
     }
     if (c->IsStringClass()) {
       // Replace calls to String.<init> with equivalent StringFactory call.
-      jmethodID sf_mid = WellKnownClasses::StringInitToStringFactoryMethodID(mid);
+      jmethodID sf_mid = soa.EncodeMethod(
+          WellKnownClasses::StringInitToStringFactory(soa.DecodeMethod(mid)));
       return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
     }
     mirror::Object* result = c->AllocObject(soa.Self());
@@ -637,13 +640,15 @@
     CHECK_NON_NULL_ARGUMENT(java_class);
     CHECK_NON_NULL_ARGUMENT(mid);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
+    ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(),
+                                                soa.Decode<mirror::Class>(java_class));
     if (c == nullptr) {
       return nullptr;
     }
     if (c->IsStringClass()) {
       // Replace calls to String.<init> with equivalent StringFactory call.
-      jmethodID sf_mid = WellKnownClasses::StringInitToStringFactoryMethodID(mid);
+      jmethodID sf_mid = soa.EncodeMethod(
+          WellKnownClasses::StringInitToStringFactory(soa.DecodeMethod(mid)));
       return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
     }
     mirror::Object* result = c->AllocObject(soa.Self());
@@ -1220,9 +1225,9 @@
     CHECK_NON_NULL_ARGUMENT(obj);
     CHECK_NON_NULL_ARGUMENT(fid);
     ScopedObjectAccess soa(env);
-    mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
     ArtField* f = soa.DecodeField(fid);
-    return soa.AddLocalReference<jobject>(f->GetObject(o));
+    return soa.AddLocalReference<jobject>(f->GetObject(o.Decode()));
   }
 
   static jobject GetStaticObjectField(JNIEnv* env, jclass, jfieldID fid) {
@@ -1236,27 +1241,27 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object);
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
     ScopedObjectAccess soa(env);
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
-    mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
+    ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
     ArtField* f = soa.DecodeField(fid);
-    f->SetObject<false>(o, v);
+    f->SetObject<false>(o.Decode(), v.Decode());
   }
 
   static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
     ScopedObjectAccess soa(env);
-    mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
+    ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
     ArtField* f = soa.DecodeField(fid);
-    f->SetObject<false>(f->GetDeclaringClass(), v);
+    f->SetObject<false>(f->GetDeclaringClass(), v.Decode());
   }
 
 #define GET_PRIMITIVE_FIELD(fn, instance) \
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
   ScopedObjectAccess soa(env); \
-  mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   ArtField* f = soa.DecodeField(fid); \
-  return f->Get ##fn (o)
+  return f->Get ##fn (o.Decode())
 
 #define GET_STATIC_PRIMITIVE_FIELD(fn) \
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
@@ -1268,9 +1273,9 @@
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
   ScopedObjectAccess soa(env); \
-  mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   ArtField* f = soa.DecodeField(fid); \
-  f->Set ##fn <false>(o, value)
+  f->Set ##fn <false>(o.Decode(), value)
 
 #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
@@ -1654,20 +1659,20 @@
   static jsize GetStringLength(JNIEnv* env, jstring java_string) {
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string);
     ScopedObjectAccess soa(env);
-    return soa.Decode<mirror::String*>(java_string)->GetLength();
+    return soa.Decode<mirror::String>(java_string)->GetLength();
   }
 
   static jsize GetStringUTFLength(JNIEnv* env, jstring java_string) {
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string);
     ScopedObjectAccess soa(env);
-    return soa.Decode<mirror::String*>(java_string)->GetUtfLength();
+    return soa.Decode<mirror::String>(java_string)->GetUtfLength();
   }
 
   static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length,
                               jchar* buf) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     if (start < 0 || length < 0 || length > s->GetLength() - start) {
       ThrowSIOOBE(soa, start, length, s->GetLength());
     } else {
@@ -1687,7 +1692,7 @@
                                  char* buf) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     if (start < 0 || length < 0 || length > s->GetLength() - start) {
       ThrowSIOOBE(soa, start, length, s->GetLength());
     } else {
@@ -1707,7 +1712,7 @@
   static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* is_copy) {
     CHECK_NON_NULL_ARGUMENT(java_string);
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     gc::Heap* heap = Runtime::Current()->GetHeap();
     if (heap->IsMovableObject(s) || s->IsCompressed()) {
       jchar* chars = new jchar[s->GetLength()];
@@ -1733,7 +1738,7 @@
   static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     if (s->IsCompressed() || (s->IsCompressed() == false && chars != s->GetValue())) {
       delete[] chars;
     }
@@ -1742,11 +1747,11 @@
   static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* is_copy) {
     CHECK_NON_NULL_ARGUMENT(java_string);
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     gc::Heap* heap = Runtime::Current()->GetHeap();
     if (heap->IsMovableObject(s)) {
       StackHandleScope<1> hs(soa.Self());
-      HandleWrapper<mirror::String> h(hs.NewHandleWrapper(&s));
+      HandleWrapperObjPtr<mirror::String> h(hs.NewHandleWrapper(&s));
       if (!kUseReadBarrier) {
         heap->IncrementDisableMovingGC(soa.Self());
       } else {
@@ -1779,7 +1784,7 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
     ScopedObjectAccess soa(env);
     gc::Heap* heap = Runtime::Current()->GetHeap();
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     if (heap->IsMovableObject(s)) {
       if (!kUseReadBarrier) {
         heap->DecrementDisableMovingGC(soa.Self());
@@ -1800,7 +1805,7 @@
       *is_copy = JNI_TRUE;
     }
     ScopedObjectAccess soa(env);
-    mirror::String* s = soa.Decode<mirror::String*>(java_string);
+    ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
     size_t byte_count = s->GetUtfLength();
     char* bytes = new char[byte_count + 1];
     CHECK(bytes != nullptr);  // bionic aborts anyway.
@@ -1823,7 +1828,7 @@
   static jsize GetArrayLength(JNIEnv* env, jarray java_array) {
     CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_array);
     ScopedObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(java_array);
+    ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(java_array);
     if (UNLIKELY(!obj->IsArrayInstance())) {
       soa.Vm()->JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
       return 0;
@@ -1835,8 +1840,8 @@
   static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index) {
     CHECK_NON_NULL_ARGUMENT(java_array);
     ScopedObjectAccess soa(env);
-    mirror::ObjectArray<mirror::Object>* array =
-        soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
+    ObjPtr<mirror::ObjectArray<mirror::Object>> array =
+        soa.Decode<mirror::ObjectArray<mirror::Object>>(java_array);
     return soa.AddLocalReference<jobject>(array->Get(index));
   }
 
@@ -1844,10 +1849,10 @@
                                     jobject java_value) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array);
     ScopedObjectAccess soa(env);
-    mirror::ObjectArray<mirror::Object>* array =
-        soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
-    mirror::Object* value = soa.Decode<mirror::Object*>(java_value);
-    array->Set<false>(index, value);
+    ObjPtr<mirror::ObjectArray<mirror::Object>> array =
+        soa.Decode<mirror::ObjectArray<mirror::Object>>(java_array);
+    ObjPtr<mirror::Object> value = soa.Decode<mirror::Object>(java_value);
+    array->Set<false>(index, value.Decode());
   }
 
   static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
@@ -1890,7 +1895,7 @@
     ScopedObjectAccess soa(env);
     mirror::Class* array_class;
     {
-      mirror::Class* element_class = soa.Decode<mirror::Class*>(element_jclass);
+      mirror::Class* element_class = soa.Decode<mirror::Class>(element_jclass).Decode();
       if (UNLIKELY(element_class->IsPrimitive())) {
         soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s",
                             PrettyDescriptor(element_class).c_str());
@@ -1907,7 +1912,7 @@
     mirror::ObjectArray<mirror::Object>* result =
         mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), array_class, length);
     if (result != nullptr && initial_element != nullptr) {
-      mirror::Object* initial_object = soa.Decode<mirror::Object*>(initial_element);
+      ObjPtr<mirror::Object> initial_object = soa.Decode<mirror::Object>(initial_element);
       if (initial_object != nullptr) {
         mirror::Class* element_class = result->GetClass()->GetComponentType();
         if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) {
@@ -1918,7 +1923,7 @@
           return nullptr;
         } else {
           for (jsize i = 0; i < length; ++i) {
-            result->SetWithoutChecks<false>(i, initial_object);
+            result->SetWithoutChecks<false>(i, initial_object.Decode());
           }
         }
       }
@@ -1933,7 +1938,7 @@
   static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray java_array, jboolean* is_copy) {
     CHECK_NON_NULL_ARGUMENT(java_array);
     ScopedObjectAccess soa(env);
-    mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
+    ObjPtr<mirror::Array> array = soa.Decode<mirror::Array>(java_array);
     if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
       soa.Vm()->JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
                           PrettyDescriptor(array->GetClass()).c_str());
@@ -1949,7 +1954,7 @@
         heap->IncrementDisableThreadFlip(soa.Self());
       }
       // Re-decode in case the object moved since IncrementDisableGC waits for GC to complete.
-      array = soa.Decode<mirror::Array*>(java_array);
+      array = soa.Decode<mirror::Array>(java_array);
     }
     if (is_copy != nullptr) {
       *is_copy = JNI_FALSE;
@@ -1961,14 +1966,14 @@
                                             jint mode) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array);
     ScopedObjectAccess soa(env);
-    mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
+    ObjPtr<mirror::Array> array = soa.Decode<mirror::Array>(java_array);
     if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
       soa.Vm()->JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
                           PrettyDescriptor(array->GetClass()).c_str());
       return;
     }
     const size_t component_size = array->GetClass()->GetComponentSize();
-    ReleasePrimitiveArray(soa, array, component_size, elements, mode);
+    ReleasePrimitiveArray(soa, array.Decode(), component_size, elements, mode);
   }
 
   static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) {
@@ -2142,7 +2147,7 @@
     }
     CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
     if (UNLIKELY(method_count == 0)) {
       LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
           << PrettyDescriptor(c);
@@ -2154,13 +2159,13 @@
       const char* sig = methods[i].signature;
       const void* fnPtr = methods[i].fnPtr;
       if (UNLIKELY(name == nullptr)) {
-        ReportInvalidJNINativeMethod(soa, c, "method name", i, return_errors);
+        ReportInvalidJNINativeMethod(soa, c.Decode(), "method name", i, return_errors);
         return JNI_ERR;
       } else if (UNLIKELY(sig == nullptr)) {
-        ReportInvalidJNINativeMethod(soa, c, "method signature", i, return_errors);
+        ReportInvalidJNINativeMethod(soa, c.Decode(), "method signature", i, return_errors);
         return JNI_ERR;
       } else if (UNLIKELY(fnPtr == nullptr)) {
-        ReportInvalidJNINativeMethod(soa, c, "native function", i, return_errors);
+        ReportInvalidJNINativeMethod(soa, c.Decode(), "native function", i, return_errors);
         return JNI_ERR;
       }
       bool is_fast = false;
@@ -2203,17 +2208,17 @@
       // the parent.
       ArtMethod* m = nullptr;
       bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled();
-      for (mirror::Class* current_class = c;
+      for (ObjPtr<mirror::Class> current_class = c;
            current_class != nullptr;
            current_class = current_class->GetSuperClass()) {
         // Search first only comparing methods which are native.
-        m = FindMethod<true>(current_class, name, sig);
+        m = FindMethod<true>(current_class.Decode(), name, sig);
         if (m != nullptr) {
           break;
         }
 
         // Search again comparing to all methods, to find non-native methods that match.
-        m = FindMethod<false>(current_class, name, sig);
+        m = FindMethod<false>(current_class.Decode(), name, sig);
         if (m != nullptr) {
           break;
         }
@@ -2226,19 +2231,23 @@
       }
 
       if (m == nullptr) {
-        LOG(return_errors ? ERROR : INTERNAL_FATAL) << "Failed to register native method "
+        c->DumpClass(
+            LOG_STREAM(return_errors
+                           ? ::android::base::ERROR
+                           : ::android::base::FATAL_WITHOUT_ABORT),
+            mirror::Class::kDumpClassFullDetail);
+        LOG(return_errors ? ::android::base::ERROR : ::android::base::FATAL)
+            << "Failed to register native method "
             << PrettyDescriptor(c) << "." << name << sig << " in "
             << c->GetDexCache()->GetLocation()->ToModifiedUtf8();
-        // Safe to pass in LOG(FATAL) since the log object aborts in destructor and only goes
-        // out of scope after the DumpClass is done executing.
-        c->DumpClass(LOG(return_errors ? ERROR : FATAL), mirror::Class::kDumpClassFullDetail);
-        ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static");
+        ThrowNoSuchMethodError(soa, c.Decode(), name, sig, "static or non-static");
         return JNI_ERR;
       } else if (!m->IsNative()) {
-        LOG(return_errors ? ERROR : FATAL) << "Failed to register non-native method "
+        LOG(return_errors ? ::android::base::ERROR : ::android::base::FATAL)
+            << "Failed to register non-native method "
             << PrettyDescriptor(c) << "." << name << sig
             << " as native";
-        ThrowNoSuchMethodError(soa, c, name, sig, "native");
+        ThrowNoSuchMethodError(soa, c.Decode(), name, sig, "native");
         return JNI_ERR;
       }
 
@@ -2253,7 +2262,7 @@
   static jint UnregisterNatives(JNIEnv* env, jclass java_class) {
     CHECK_NON_NULL_ARGUMENT_RETURN(java_class, JNI_ERR);
     ScopedObjectAccess soa(env);
-    mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+    ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
 
     VLOG(jni) << "[Unregistering JNI native methods for " << PrettyClass(c) << "]";
 
@@ -2276,24 +2285,24 @@
   static jint MonitorEnter(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS {
     CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR);
     ScopedObjectAccess soa(env);
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     o = o->MonitorEnter(soa.Self());
     if (soa.Self()->IsExceptionPending()) {
       return JNI_ERR;
     }
-    soa.Env()->monitors.Add(o);
+    soa.Env()->monitors.Add(o.Decode());
     return JNI_OK;
   }
 
   static jint MonitorExit(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS {
     CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR);
     ScopedObjectAccess soa(env);
-    mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     o->MonitorExit(soa.Self());
     if (soa.Self()->IsExceptionPending()) {
       return JNI_ERR;
     }
-    soa.Env()->monitors.Remove(o);
+    soa.Env()->monitors.Remove(o.Decode());
     return JNI_OK;
   }
 
@@ -2402,7 +2411,7 @@
   static ArtArrayT* DecodeAndCheckArrayType(ScopedObjectAccess& soa, JArrayT java_array,
                                            const char* fn_name, const char* operation)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array);
+    ObjPtr<ArtArrayT> array = soa.Decode<ArtArrayT>(java_array);
     if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) {
       soa.Vm()->JniAbortF(fn_name,
                           "attempt to %s %s primitive array elements with an object of type %s",
@@ -2412,7 +2421,7 @@
       return nullptr;
     }
     DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize());
-    return array;
+    return array.Decode();
   }
 
   template <typename ArrayT, typename ElementT, typename ArtArrayT>
@@ -2478,7 +2487,7 @@
       } else if (kWarnJniAbort && memcmp(array_data, elements, bytes) != 0) {
         // Warn if we have JNI_ABORT and the arrays don't match since this is usually an error.
         LOG(WARNING) << "Possible incorrect JNI_ABORT in Release*ArrayElements";
-        soa.Self()->DumpJavaStack(LOG(WARNING));
+        soa.Self()->DumpJavaStack(LOG_STREAM(WARNING));
       }
     }
     if (mode != JNI_COMMIT) {
@@ -3042,7 +3051,7 @@
     os << "JNIWeakGlobalRefType";
     return os;
   default:
-    LOG(::art::FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]";
+    LOG(FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]";
     UNREACHABLE();
   }
 }
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index fe0081c..fbd670c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -22,7 +22,7 @@
 #include "java_vm_ext.h"
 #include "jni_env_ext.h"
 #include "mirror/string-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 
 namespace art {
@@ -58,7 +58,7 @@
   void ExpectException(jclass exception_class) {
     ScopedObjectAccess soa(env_);
     EXPECT_TRUE(env_->ExceptionCheck())
-        << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class));
+        << PrettyDescriptor(soa.Decode<mirror::Class>(exception_class));
     jthrowable exception = env_->ExceptionOccurred();
     EXPECT_NE(nullptr, exception);
     env_->ExceptionClear();
@@ -619,7 +619,7 @@
         class_loader_ = LoadDex("MyClassNatives");
         StackHandleScope<1> hs(soa.Self());
         Handle<mirror::ClassLoader> loader(
-            hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
+            hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
         mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
         const auto pointer_size = class_linker_->GetImagePointerSize();
         ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
@@ -679,7 +679,12 @@
   ASSERT_TRUE(env_->IsInstanceOf(o, c));
   // ...whose fields haven't been initialized because
   // we didn't call a constructor.
-  ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
+  if (art::mirror::kUseStringCompression) {
+    // Zero-length string is compressed, so the length internally will be -(1 << 31).
+    ASSERT_EQ(-2147483648, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
+  } else {
+    ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
+  }
 }
 
 TEST_F(JniInternalTest, GetVersion) {
@@ -1598,7 +1603,7 @@
 TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) {
   jstring s = env_->NewStringUTF("hello");
   ScopedObjectAccess soa(env_);
-  mirror::String* s_m = soa.Decode<mirror::String*>(s);
+  ObjPtr<mirror::String> s_m = soa.Decode<mirror::String>(s);
   ASSERT_TRUE(s != nullptr);
 
   jchar expected[] = { 'h', 'e', 'l', 'l', 'o' };
@@ -2236,7 +2241,7 @@
 
 static bool IsLocked(JNIEnv* env, jobject jobj) {
   ScopedObjectAccess soa(env);
-  LockWord lock_word = soa.Decode<mirror::Object*>(jobj)->GetLockWord(true);
+  LockWord lock_word = soa.Decode<mirror::Object>(jobj)->GetLockWord(true);
   switch (lock_word.GetState()) {
     case LockWord::kHashCode:
     case LockWord::kUnlocked:
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
index 1f424b3..443f095 100644
--- a/runtime/jobject_comparator.cc
+++ b/runtime/jobject_comparator.cc
@@ -19,7 +19,7 @@
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -32,8 +32,8 @@
   }
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object*>(jobj1)));
-  Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object*>(jobj2)));
+  Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1)));
+  Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2)));
   if (obj1.Get() == nullptr) {
     return true;
   } else if (obj2.Get() == nullptr) {
diff --git a/runtime/mirror/abstract_method.h b/runtime/mirror/abstract_method.h
deleted file mode 100644
index 9c20613..0000000
--- a/runtime/mirror/abstract_method.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_
-#define ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_
-
-#include "executable.h"
-#include "gc_root.h"
-#include "object.h"
-#include "object_callbacks.h"
-#include "read_barrier_option.h"
-
-namespace art {
-
-struct AbstractMethodOffsets;
-class ArtMethod;
-
-namespace mirror {
-
-// C++ mirror of java.lang.reflect.AbstractMethod.
-class MANAGED AbstractMethod : public Executable {
- public:
-  // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod.
-  template <PointerSize kPointerSize, bool kTransactionActive>
-  bool CreateFromArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Roles::uninterruptible_);
-
-  ArtMethod* GetArtMethod() REQUIRES_SHARED(Locks::mutator_lock_);
-  // Only used by the image writer.
-  template <bool kTransactionActive = false>
-  void SetArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
-  mirror::Class* GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
-
- private:
-  static MemberOffset ArtMethodOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, art_method_));
-  }
-  static MemberOffset DeclaringClassOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, declaring_class_));
-  }
-  static MemberOffset DeclaringClassOfOverriddenMethodOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, declaring_class_of_overridden_method_));
-  }
-  static MemberOffset AccessFlagsOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, access_flags_));
-  }
-  static MemberOffset DexMethodIndexOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(AbstractMethod, dex_method_index_));
-  }
-
-  HeapReference<mirror::Class> declaring_class_;
-  HeapReference<mirror::Class> declaring_class_of_overridden_method_;
-  uint64_t art_method_;
-  uint32_t access_flags_;
-  uint32_t dex_method_index_;
-
-  friend struct art::AbstractMethodOffsets;  // for verifying offset information
-  DISALLOW_IMPLICIT_CONSTRUCTORS(AbstractMethod);
-};
-
-}  // namespace mirror
-}  // namespace art
-
-#endif  // ART_RUNTIME_MIRROR_ABSTRACT_METHOD_H_
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index f21baed..2e5f532 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -147,8 +147,8 @@
 
 void Class::SetClassSize(uint32_t new_class_size) {
   if (kIsDebugBuild && new_class_size < GetClassSize()) {
-    DumpClass(LOG(INTERNAL_FATAL), kDumpClassFullDetail);
-    LOG(INTERNAL_FATAL) << new_class_size << " vs " << GetClassSize();
+    DumpClass(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpClassFullDetail);
+    LOG(FATAL_WITHOUT_ABORT) << new_class_size << " vs " << GetClassSize();
     LOG(FATAL) << "class=" << PrettyTypeOf(this);
   }
   // Not called within a transaction.
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 548087e..6c1259b 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -54,9 +54,10 @@
 class DexCache;
 class IfTable;
 class Method;
-struct StringDexCachePair;
+template <typename T> struct PACKED(8) DexCachePair;
 
-using StringDexCacheType = std::atomic<mirror::StringDexCachePair>;
+using StringDexCachePair = DexCachePair<mirror::String>;
+using StringDexCacheType = std::atomic<StringDexCachePair>;
 
 // C++ mirror of java.lang.Class
 class MANAGED Class FINAL : public Object {
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 220979a..b388f65 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -25,6 +25,7 @@
 #include "base/enums.h"
 #include "base/logging.h"
 #include "mirror/class.h"
+#include "mirror/method_type.h"
 #include "runtime.h"
 
 #include <atomic>
@@ -39,14 +40,11 @@
 
 inline mirror::String* DexCache::GetResolvedString(uint32_t string_idx) {
   DCHECK_LT(string_idx, GetDexFile()->NumStringIds());
-  return StringDexCachePair::LookupString(GetStrings(), string_idx, NumStrings()).Read();
+  return StringDexCachePair::Lookup(GetStrings(), string_idx, NumStrings()).Read();
 }
 
 inline void DexCache::SetResolvedString(uint32_t string_idx, mirror::String* resolved) {
-  DCHECK_LT(string_idx % NumStrings(), NumStrings());
-  GetStrings()[string_idx % NumStrings()].store(
-      StringDexCachePair(resolved, string_idx),
-      std::memory_order_relaxed);
+  StringDexCachePair::Assign(GetStrings(), string_idx, resolved, NumStrings());
   Runtime* const runtime = Runtime::Current();
   if (UNLIKELY(runtime->IsActiveTransaction())) {
     DCHECK(runtime->IsAotCompiler());
@@ -61,10 +59,10 @@
   DCHECK(Runtime::Current()->IsAotCompiler());
   StringDexCacheType* slot = &GetStrings()[slot_idx];
   // This is racy but should only be called from the transactional interpreter.
-  if (slot->load(std::memory_order_relaxed).string_index == string_idx) {
+  if (slot->load(std::memory_order_relaxed).index == string_idx) {
     StringDexCachePair cleared(
         nullptr,
-        StringDexCachePair::InvalidStringIndexForSlot(slot_idx));
+        StringDexCachePair::InvalidIndexForSlot(slot_idx));
     slot->store(cleared, std::memory_order_relaxed);
   }
 }
@@ -82,6 +80,23 @@
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
 
+inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+  return MethodTypeDexCachePair::Lookup(
+      GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read();
+}
+
+inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+  DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+  DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+
+  MethodTypeDexCachePair::Assign(GetResolvedMethodTypes(), proto_idx, resolved,
+                                 NumResolvedMethodTypes());
+  // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
 inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
   DCHECK_LT(field_idx, NumResolvedFields());  // NOTE: Unchecked, i.e. not throwing AIOOB.
@@ -155,11 +170,11 @@
     mirror::StringDexCacheType* strings = GetStrings();
     for (size_t i = 0, num_strings = NumStrings(); i != num_strings; ++i) {
       StringDexCachePair source = strings[i].load(std::memory_order_relaxed);
-      mirror::String* before = source.string_pointer.Read<kReadBarrierOption>();
+      mirror::String* before = source.object.Read<kReadBarrierOption>();
       GcRoot<mirror::String> root(before);
       visitor.VisitRootIfNonNull(root.AddressWithoutBarrier());
       if (root.Read() != before) {
-        source.string_pointer = GcRoot<String>(root.Read());
+        source.object = GcRoot<String>(root.Read());
         strings[i].store(source, std::memory_order_relaxed);
       }
     }
@@ -175,9 +190,9 @@
   mirror::StringDexCacheType* src = GetStrings();
   for (size_t i = 0, count = NumStrings(); i < count; ++i) {
     StringDexCachePair source = src[i].load(std::memory_order_relaxed);
-    mirror::String* ptr = source.string_pointer.Read<kReadBarrierOption>();
+    mirror::String* ptr = source.object.Read<kReadBarrierOption>();
     mirror::String* new_source = visitor(ptr);
-    source.string_pointer = GcRoot<String>(new_source);
+    source.object = GcRoot<String>(new_source);
     dest[i].store(source, std::memory_order_relaxed);
   }
 }
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index cfcec9c..66f858c 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -41,6 +41,8 @@
                     uint32_t num_resolved_methods,
                     ArtField** resolved_fields,
                     uint32_t num_resolved_fields,
+                    MethodTypeDexCacheType* resolved_method_types,
+                    uint32_t num_resolved_method_types,
                     PointerSize pointer_size) {
   CHECK(dex_file != nullptr);
   CHECK(location != nullptr);
@@ -48,6 +50,7 @@
   CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr);
   CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
   CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
+  CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
 
   SetDexFile(dex_file);
   SetLocation(location);
@@ -55,10 +58,12 @@
   SetResolvedTypes(resolved_types);
   SetResolvedMethods(resolved_methods);
   SetResolvedFields(resolved_fields);
+  SetResolvedMethodTypes(resolved_method_types);
   SetField32<false>(NumStringsOffset(), num_strings);
   SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
   SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
   SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
+  SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
 
   Runtime* const runtime = Runtime::Current();
   if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 7d4021f..2fcabb5 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -33,11 +33,12 @@
 
 namespace mirror {
 
+class MethodType;
 class String;
 
-struct PACKED(8) StringDexCachePair {
-  GcRoot<String> string_pointer;
-  uint32_t string_index;
+template <typename T> struct PACKED(8) DexCachePair {
+  GcRoot<T> object;
+  uint32_t index;
   // The array is initially [ {0,0}, {0,0}, {0,0} ... ]
   // We maintain the invariant that once a dex cache entry is populated,
   // the pointer is always non-0
@@ -45,49 +46,65 @@
   // {non-0, non-0} OR {0,0}
   //
   // It's generally sufficiently enough then to check if the
-  // lookup string index matches the stored string index (for a >0 string index)
+  // lookup index matches the stored index (for a >0 lookup index)
   // because if it's true the pointer is also non-null.
   //
   // For the 0th entry which is a special case, the value is either
   // {0,0} (initial state) or {non-0, 0} which indicates
-  // that a valid string is stored at that index for a dex string id of 0.
+  // that a valid object is stored at that index for a dex section id of 0.
   //
-  // As an optimization, we want to avoid branching on the string pointer since
-  // it's always non-null if the string id branch succeeds (except for the 0th string id).
+  // As an optimization, we want to avoid branching on the object pointer since
+  // it's always non-null if the id branch succeeds (except for the 0th id).
   // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
-  // the lookup string id == stored id branch.
-  StringDexCachePair(String* string, uint32_t string_idx)
-      : string_pointer(string),
-        string_index(string_idx) {}
-  StringDexCachePair() = default;
-  StringDexCachePair(const StringDexCachePair&) = default;
-  StringDexCachePair& operator=(const StringDexCachePair&) = default;
+  // the lookup id == stored id branch.
+  DexCachePair(T* object, uint32_t index)
+      : object(object),
+        index(index) {}
+  DexCachePair() = default;
+  DexCachePair(const DexCachePair<T>&) = default;
+  DexCachePair& operator=(const DexCachePair<T>&) = default;
 
-  static void Initialize(StringDexCacheType* strings) {
-    mirror::StringDexCachePair first_elem;
-    first_elem.string_pointer = GcRoot<String>(nullptr);
-    first_elem.string_index = InvalidStringIndexForSlot(0);
-    strings[0].store(first_elem, std::memory_order_relaxed);
+  static void Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
+    DexCachePair<T> first_elem;
+    first_elem.object = GcRoot<T>(nullptr);
+    first_elem.index = InvalidIndexForSlot(0);
+    dex_cache[0].store(first_elem, std::memory_order_relaxed);
   }
 
-  static GcRoot<String> LookupString(StringDexCacheType* dex_cache,
-                                     uint32_t string_idx,
-                                     uint32_t cache_size) {
-    StringDexCachePair index_string = dex_cache[string_idx % cache_size]
-        .load(std::memory_order_relaxed);
-    if (string_idx != index_string.string_index) return GcRoot<String>(nullptr);
-    DCHECK(!index_string.string_pointer.IsNull());
-    return index_string.string_pointer;
+  static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
+                          uint32_t idx,
+                          uint32_t cache_size) {
+    DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
+    if (idx != element.index) {
+      return GcRoot<T>(nullptr);
+    }
+
+    DCHECK(!element.object.IsNull());
+    return element.object;
   }
 
-  static uint32_t InvalidStringIndexForSlot(uint32_t slot) {
+  static void Assign(std::atomic<DexCachePair<T>>* dex_cache,
+                     uint32_t idx,
+                     T* object,
+                     uint32_t cache_size) {
+    DCHECK_LT(idx % cache_size, cache_size);
+    dex_cache[idx % cache_size].store(
+        DexCachePair<T>(object, idx), std::memory_order_relaxed);
+  }
+
+  static uint32_t InvalidIndexForSlot(uint32_t slot) {
     // Since the cache size is a power of two, 0 will always map to slot 0.
     // Use 1 for slot 0 and 0 for all other slots.
     return (slot == 0) ? 1u : 0u;
   }
 };
+
+using StringDexCachePair = DexCachePair<mirror::String>;
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
+using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>;
+using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
+
 // C++ mirror of java.lang.DexCache.
 class MANAGED DexCache FINAL : public Object {
  public:
@@ -99,10 +116,20 @@
   static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
                 "String dex cache size is not a power of 2.");
 
+  // Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions
+  // to hold.
+  static constexpr size_t kDexCacheMethodTypeCacheSize = 1024;
+  static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
+                "MethodType dex cache size is not a power of 2.");
+
   static constexpr size_t StaticStringSize() {
     return kDexCacheStringCacheSize;
   }
 
+  static constexpr size_t StaticMethodTypeSize() {
+    return kDexCacheMethodTypeCacheSize;
+  }
+
   // Size of an instance of java.lang.DexCache not including referenced values.
   static constexpr uint32_t InstanceSize() {
     return sizeof(DexCache);
@@ -118,6 +145,8 @@
             uint32_t num_resolved_methods,
             ArtField** resolved_fields,
             uint32_t num_resolved_fields,
+            MethodTypeDexCacheType* resolved_methodtypes,
+            uint32_t num_resolved_methodtypes,
             PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void Fixup(ArtMethod* trampoline, PointerSize pointer_size)
@@ -155,6 +184,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
   }
 
+  static MemberOffset ResolvedMethodTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
+  }
+
   static MemberOffset NumStringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
   }
@@ -171,6 +204,10 @@
     return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
   }
 
+  static MemberOffset NumResolvedMethodTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
+  }
+
   mirror::String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -201,6 +238,10 @@
   ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+
   StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
   }
@@ -239,6 +280,17 @@
     SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
   }
 
+  MethodTypeDexCacheType* GetResolvedMethodTypes()
+      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldPtr<MethodTypeDexCacheType*>(ResolvedMethodTypesOffset());
+  }
+
+  void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
+      ALWAYS_INLINE
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
+  }
+
   size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetField32(NumStringsOffset());
   }
@@ -255,6 +307,10 @@
     return GetField32(NumResolvedFieldsOffset());
   }
 
+  size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetField32(NumResolvedMethodTypesOffset());
+  }
+
   const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
   }
@@ -286,16 +342,20 @@
 
   HeapReference<Object> dex_;
   HeapReference<String> location_;
-  uint64_t dex_file_;           // const DexFile*
-  uint64_t resolved_fields_;    // ArtField*, array with num_resolved_fields_ elements.
-  uint64_t resolved_methods_;   // ArtMethod*, array with num_resolved_methods_ elements.
-  uint64_t resolved_types_;     // GcRoot<Class>*, array with num_resolved_types_ elements.
-  uint64_t strings_;            // std::atomic<StringDexCachePair>*,
-                                // array with num_strings_ elements.
-  uint32_t num_resolved_fields_;    // Number of elements in the resolved_fields_ array.
-  uint32_t num_resolved_methods_;   // Number of elements in the resolved_methods_ array.
-  uint32_t num_resolved_types_;     // Number of elements in the resolved_types_ array.
-  uint32_t num_strings_;            // Number of elements in the strings_ array.
+  uint64_t dex_file_;               // const DexFile*
+  uint64_t resolved_fields_;        // ArtField*, array with num_resolved_fields_ elements.
+  uint64_t resolved_method_types_;  // std::atomic<MethodTypeDexCachePair>* array with
+                                    // num_resolved_method_types_ elements.
+  uint64_t resolved_methods_;       // ArtMethod*, array with num_resolved_methods_ elements.
+  uint64_t resolved_types_;         // GcRoot<Class>*, array with num_resolved_types_ elements.
+  uint64_t strings_;                // std::atomic<StringDexCachePair>*, array with num_strings_
+                                    // elements.
+
+  uint32_t num_resolved_fields_;        // Number of elements in the resolved_fields_ array.
+  uint32_t num_resolved_method_types_;  // Number of elements in the resolved_method_types_ array.
+  uint32_t num_resolved_methods_;       // Number of elements in the resolved_methods_ array.
+  uint32_t num_resolved_types_;         // Number of elements in the resolved_types_ array.
+  uint32_t num_strings_;                // Number of elements in the strings_ array.
 
   friend struct art::DexCacheOffsets;  // for verifying offset information
   friend class Object;  // For VisitReferences
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 43ba362..12301b8 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -24,13 +24,21 @@
 #include "mirror/class_loader-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "handle_scope-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace mirror {
 
 class DexCacheTest : public CommonRuntimeTest {};
 
+class DexCacheMethodHandlesTest : public DexCacheTest {
+ protected:
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    CommonRuntimeTest::SetUpRuntimeOptions(options);
+    options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr));
+  }
+};
+
 TEST_F(DexCacheTest, Open) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
@@ -46,21 +54,35 @@
   EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),   dex_cache->NumResolvedTypes());
   EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
+  // This should always be zero because the -Xexperimental:method-handles isn't
+  // set.
+  EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes());
+}
+
+TEST_F(DexCacheMethodHandlesTest, Open) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+  Handle<DexCache> dex_cache(
+      hs.NewHandle(class_linker_->AllocDexCache(soa.Self(),
+                                                *java_lang_dex_file_,
+                                                Runtime::Current()->GetLinearAlloc())));
+
+  EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
+      || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
 }
 
 TEST_F(DexCacheTest, LinearAlloc) {
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader(LoadDex("Main"));
   ASSERT_TRUE(jclass_loader != nullptr);
-  Runtime* const runtime = Runtime::Current();
-  ClassLinker* const class_linker = runtime->GetClassLinker();
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(jclass_loader)));
-  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LMain;", class_loader);
+      soa.Decode<mirror::ClassLoader>(jclass_loader)));
+  mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
   ASSERT_TRUE(klass != nullptr);
   LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator();
-  EXPECT_NE(linear_alloc, runtime->GetLinearAlloc());
+  EXPECT_NE(linear_alloc, runtime_->GetLinearAlloc());
   EXPECT_TRUE(linear_alloc->Contains(klass->GetDexCache()->GetResolvedMethods()));
 }
 
@@ -68,16 +90,14 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader(LoadDex("Packages"));
   ASSERT_TRUE(jclass_loader != nullptr);
-  Runtime* const runtime = Runtime::Current();
-  ClassLinker* const class_linker = runtime->GetClassLinker();
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      soa.Decode<mirror::ClassLoader>(jclass_loader)));
   Handle<mirror::Class> klass1 =
-      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
   ASSERT_TRUE(klass1.Get() != nullptr);
   Handle<mirror::Class> klass2 =
-      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
   ASSERT_TRUE(klass2.Get() != nullptr);
   EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache());
 
@@ -92,5 +112,60 @@
   }
 }
 
+TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader(LoadDex("MethodTypes"));
+  ASSERT_TRUE(jclass_loader != nullptr);
+
+  StackHandleScope<5> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+  Handle<mirror::Class> method_types(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
+  class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
+
+  ArtMethod* method1 = method_types->FindVirtualMethod(
+      "method1",
+      "(Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+  ArtMethod* method2 = method_types->FindVirtualMethod(
+      "method2",
+      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+      kRuntimePointerSize);
+
+  const DexFile& dex_file = *(method1->GetDexFile());
+  Handle<mirror::DexCache> dex_cache = hs.NewHandle(
+      class_linker_->FindDexCache(Thread::Current(), dex_file));
+
+  const DexFile::MethodId& method1_id = dex_file.GetMethodId(method1->GetDexMethodIndex());
+  const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex());
+
+  Handle<mirror::MethodType> method1_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
+  Handle<mirror::MethodType> method2_type = hs.NewHandle(
+      class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader));
+
+  EXPECT_EQ(method1_type.Get(), dex_cache->GetResolvedMethodType(method1_id.proto_idx_));
+  EXPECT_EQ(method2_type.Get(), dex_cache->GetResolvedMethodType(method2_id.proto_idx_));
+
+  // The MethodTypes dex file contains a single interface with two abstract
+  // methods. It must therefore contain precisely two method IDs.
+  ASSERT_EQ(2u, dex_file.NumProtoIds());
+  ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypes());
+  MethodTypeDexCacheType* method_types_cache = dex_cache->GetResolvedMethodTypes();
+
+  for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) {
+    const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed);
+    if (pair.index == method1_id.proto_idx_) {
+      ASSERT_EQ(method1_type.Get(), pair.object.Read());
+    } else if (pair.index == method2_id.proto_idx_) {
+      ASSERT_EQ(method2_type.Get(), pair.object.Read());
+    } else {
+      ASSERT_TRUE(false);
+    }
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/abstract_method.cc b/runtime/mirror/executable.cc
similarity index 70%
rename from runtime/mirror/abstract_method.cc
rename to runtime/mirror/executable.cc
index b4dce58..33ebd81 100644
--- a/runtime/mirror/abstract_method.cc
+++ b/runtime/mirror/executable.cc
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#include "abstract_method.h"
-
 #include "art_method-inl.h"
+#include "executable.h"
 
 namespace art {
 namespace mirror {
 
 template <PointerSize kPointerSize, bool kTransactionActive>
-bool AbstractMethod::CreateFromArtMethod(ArtMethod* method) {
+bool Executable::CreateFromArtMethod(ArtMethod* method) {
   auto* interface_method = method->GetInterfaceMethodIfProxy(kPointerSize);
   SetArtMethod<kTransactionActive>(method);
   SetFieldObject<kTransactionActive>(DeclaringClassOffset(), method->GetDeclaringClass());
@@ -33,28 +32,28 @@
   return true;
 }
 
-template bool AbstractMethod::CreateFromArtMethod<PointerSize::k32, false>(
+template bool Executable::CreateFromArtMethod<PointerSize::k32, false>(
     ArtMethod* method);
-template bool AbstractMethod::CreateFromArtMethod<PointerSize::k32, true>(
+template bool Executable::CreateFromArtMethod<PointerSize::k32, true>(
     ArtMethod* method);
-template bool AbstractMethod::CreateFromArtMethod<PointerSize::k64, false>(
+template bool Executable::CreateFromArtMethod<PointerSize::k64, false>(
     ArtMethod* method);
-template bool AbstractMethod::CreateFromArtMethod<PointerSize::k64, true>(
+template bool Executable::CreateFromArtMethod<PointerSize::k64, true>(
     ArtMethod* method);
 
-ArtMethod* AbstractMethod::GetArtMethod() {
+ArtMethod* Executable::GetArtMethod() {
   return reinterpret_cast<ArtMethod*>(GetField64(ArtMethodOffset()));
 }
 
 template <bool kTransactionActive>
-void AbstractMethod::SetArtMethod(ArtMethod* method) {
+void Executable::SetArtMethod(ArtMethod* method) {
   SetField64<kTransactionActive>(ArtMethodOffset(), reinterpret_cast<uint64_t>(method));
 }
 
-template void AbstractMethod::SetArtMethod<false>(ArtMethod* method);
-template void AbstractMethod::SetArtMethod<true>(ArtMethod* method);
+template void Executable::SetArtMethod<false>(ArtMethod* method);
+template void Executable::SetArtMethod<true>(ArtMethod* method);
 
-mirror::Class* AbstractMethod::GetDeclaringClass() {
+mirror::Class* Executable::GetDeclaringClass() {
   return GetFieldObject<mirror::Class>(DeclaringClassOffset());
 }
 
diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h
index 232fce8..6c465f6 100644
--- a/runtime/mirror/executable.h
+++ b/runtime/mirror/executable.h
@@ -32,9 +32,42 @@
 
 // C++ mirror of java.lang.reflect.Executable.
 class MANAGED Executable : public AccessibleObject {
+ public:
+  // Called from Constructor::CreateFromArtMethod, Method::CreateFromArtMethod.
+  template <PointerSize kPointerSize, bool kTransactionActive>
+  bool CreateFromArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Roles::uninterruptible_);
+
+  ArtMethod* GetArtMethod() REQUIRES_SHARED(Locks::mutator_lock_);
+  // Only used by the image writer.
+  template <bool kTransactionActive = false>
+  void SetArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+  mirror::Class* GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   uint16_t has_real_parameter_data_;
+  HeapReference<mirror::Class> declaring_class_;
+  HeapReference<mirror::Class> declaring_class_of_overridden_method_;
   HeapReference<mirror::Array> parameters_;
+  uint64_t art_method_;
+  uint32_t access_flags_;
+  uint32_t dex_method_index_;
+
+  static MemberOffset ArtMethodOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Executable, art_method_));
+  }
+  static MemberOffset DeclaringClassOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Executable, declaring_class_));
+  }
+  static MemberOffset DeclaringClassOfOverriddenMethodOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Executable, declaring_class_of_overridden_method_));
+  }
+  static MemberOffset AccessFlagsOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Executable, access_flags_));
+  }
+  static MemberOffset DexMethodIndexOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Executable, dex_method_index_));
+  }
 
   friend struct art::ExecutableOffsets;  // for verifying offset information
   DISALLOW_IMPLICIT_CONSTRUCTORS(Executable);
diff --git a/runtime/mirror/method.cc b/runtime/mirror/method.cc
index ef16719..71bac7e 100644
--- a/runtime/mirror/method.cc
+++ b/runtime/mirror/method.cc
@@ -56,7 +56,7 @@
   DCHECK(!method->IsConstructor()) << PrettyMethod(method);
   auto* ret = down_cast<Method*>(StaticClass()->AllocObject(self));
   if (LIKELY(ret != nullptr)) {
-    static_cast<AbstractMethod*>(ret)->
+    static_cast<Executable*>(ret)->
         CreateFromArtMethod<kPointerSize, kTransactionActive>(method);
   }
   return ret;
@@ -108,7 +108,7 @@
   DCHECK(method->IsConstructor()) << PrettyMethod(method);
   auto* ret = down_cast<Constructor*>(StaticClass()->AllocObject(self));
   if (LIKELY(ret != nullptr)) {
-    static_cast<AbstractMethod*>(ret)->
+    static_cast<Executable*>(ret)->
         CreateFromArtMethod<kPointerSize, kTransactionActive>(method);
   }
   return ret;
diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h
index 6881991..205ea7a 100644
--- a/runtime/mirror/method.h
+++ b/runtime/mirror/method.h
@@ -17,8 +17,8 @@
 #ifndef ART_RUNTIME_MIRROR_METHOD_H_
 #define ART_RUNTIME_MIRROR_METHOD_H_
 
-#include "abstract_method.h"
 #include "gc_root.h"
+#include "executable.h"
 
 namespace art {
 namespace mirror {
@@ -26,7 +26,7 @@
 class Class;
 
 // C++ mirror of java.lang.reflect.Method.
-class MANAGED Method : public AbstractMethod {
+class MANAGED Method : public Executable {
  public:
   template <PointerSize kPointerSize, bool kTransactionActive>
   static Method* CreateFromArtMethod(Thread* self, ArtMethod* method)
@@ -58,7 +58,7 @@
 };
 
 // C++ mirror of java.lang.reflect.Constructor.
-class MANAGED Constructor: public AbstractMethod {
+class MANAGED Constructor: public Executable {
  public:
   template <PointerSize kPointerSize, bool kTransactionActive>
   static Constructor* CreateFromArtMethod(Thread* self, ArtMethod* method)
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
new file mode 100644
index 0000000..fdfaaa8
--- /dev/null
+++ b/runtime/mirror/method_handle_impl.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "method_handle_impl.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodHandleImpl::static_class_;
+
+void MethodHandleImpl::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodHandleImpl::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodHandleImpl::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
new file mode 100644
index 0000000..a0aae3c
--- /dev/null
+++ b/runtime/mirror/method_handle_impl.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
+#define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
+
+#include "class.h"
+#include "gc_root.h"
+#include "object.h"
+#include "method_type.h"
+
+namespace art {
+
+struct MethodHandleImplOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodHandle
+class MANAGED MethodHandle : public Object {
+ public:
+  mirror::MethodType* GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_));
+  }
+
+  ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return reinterpret_cast<ArtMethod*>(
+        GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
+  }
+
+ private:
+  HeapReference<mirror::Object> as_type_cache_;
+  HeapReference<mirror::MethodType> method_type_;
+  uint64_t art_field_or_method_;
+  uint32_t handle_kind_;
+
+ private:
+  static MemberOffset AsTypeCacheOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, as_type_cache_));
+  }
+  static MemberOffset MethodTypeOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_));
+  }
+  static MemberOffset ArtFieldOrMethodOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, art_field_or_method_));
+  }
+  static MemberOffset HandleKindOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_));
+  }
+
+  friend struct art::MethodHandleImplOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandle);
+};
+
+// C++ mirror of java.lang.invoke.MethodHandleImpl
+class MANAGED MethodHandleImpl : public MethodHandle {
+ public:
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodHandleImpl.class
+
+  friend struct art::MethodHandleImplOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandleImpl);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
new file mode 100644
index 0000000..ba6ea5e
--- /dev/null
+++ b/runtime/mirror/method_type.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "method_type.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodType::static_class_;
+
+mirror::MethodType* MethodType::Create(Thread* const self,
+                                       Handle<Class> return_type,
+                                       Handle<ObjectArray<Class>> param_types) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodType> mt(
+      hs.NewHandle(static_cast<MethodType*>(StaticClass()->AllocObject(self))));
+
+  // TODO: Do we ever create a MethodType during a transaction ? There doesn't
+  // seem like a good reason to do a polymorphic invoke that results in the
+  // resolution of a method type in an unstarted runtime.
+  mt->SetFieldObject<false>(FormOffset(), nullptr);
+  mt->SetFieldObject<false>(MethodDescriptorOffset(), nullptr);
+  mt->SetFieldObject<false>(RTypeOffset(), return_type.Get());
+  mt->SetFieldObject<false>(PTypesOffset(), param_types.Get());
+  mt->SetFieldObject<false>(WrapAltOffset(), nullptr);
+
+  return mt.Get();
+}
+
+bool MethodType::IsExactMatch(mirror::MethodType* other) REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (GetRType() != other->GetRType()) {
+    return false;
+  }
+
+  mirror::ObjectArray<Class>* const p_types = GetPTypes();
+  const int32_t params_length = p_types->GetLength();
+
+  mirror::ObjectArray<Class>* const other_p_types = other->GetPTypes();
+  if (params_length != other_p_types->GetLength()) {
+    return false;
+  }
+
+  for (int32_t i = 0; i < params_length; ++i) {
+    if (p_types->GetWithoutChecks(i) != other_p_types->GetWithoutChecks(i)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void MethodType::SetClass(Class* klass) {
+  CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+  CHECK(klass != nullptr);
+  static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodType::ResetClass() {
+  CHECK(!static_class_.IsNull());
+  static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodType::VisitRoots(RootVisitor* visitor) {
+  static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
new file mode 100644
index 0000000..5b50409
--- /dev/null
+++ b/runtime/mirror/method_type.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_MIRROR_METHOD_TYPE_H_
+#define ART_RUNTIME_MIRROR_METHOD_TYPE_H_
+
+#include "object.h"
+#include "string.h"
+#include "mirror/object_array.h"
+#include "utils.h"
+
+namespace art {
+
+struct MethodTypeOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodType
+class MANAGED MethodType : public Object {
+ public:
+  static mirror::MethodType* Create(Thread* const self,
+                                    Handle<Class> return_type,
+                                    Handle<ObjectArray<Class>> param_types)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+  static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return static_class_.Read();
+  }
+
+  ObjectArray<Class>* GetPTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
+  }
+
+  Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_));
+  }
+
+  static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Returns true iff. |other| is an exact match for this method type, i.e
+  // iff. they have the same return types and parameter types.
+  bool IsExactMatch(mirror::MethodType* other) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  static MemberOffset FormOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodType, form_));
+  }
+
+  static MemberOffset MethodDescriptorOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodType, method_descriptor_));
+  }
+
+  static MemberOffset PTypesOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodType, p_types_));
+  }
+
+  static MemberOffset RTypeOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodType, r_type_));
+  }
+
+  static MemberOffset WrapAltOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodType, wrap_alt_));
+  }
+
+  HeapReference<mirror::Object> form_;  // Unused in the runtime
+  HeapReference<mirror::String> method_descriptor_;  // Unused in the runtime
+  HeapReference<ObjectArray<mirror::Class>> p_types_;
+  HeapReference<mirror::Class> r_type_;
+  HeapReference<mirror::Object> wrap_alt_;  // Unused in the runtime
+
+  static GcRoot<mirror::Class> static_class_;  // java.lang.invoke.MethodType.class
+
+  friend struct art::MethodTypeOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(MethodType);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_METHOD_TYPE_H_
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
new file mode 100644
index 0000000..a968bff
--- /dev/null
+++ b/runtime/mirror/method_type_test.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "method_type.h"
+
+#include <string>
+#include <vector>
+
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
+#include "runtime/mirror/class.h"
+#include "runtime/mirror/class_loader.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace mirror {
+
+class MethodTypeTest : public CommonRuntimeTest {};
+
+static std::string FullyQualifiedType(const std::string& shorthand) {
+  return "Ljava/lang/" + shorthand + ";";
+}
+
+static mirror::MethodType* CreateMethodType(const std::string& return_type,
+                                            const std::vector<std::string>& param_types) {
+  CHECK_LT(param_types.size(), 3u);
+
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  Thread* const self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  StackHandleScope<5> hs(soa.Self());
+
+  Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+
+  Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass(
+          soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader));
+  CHECK(return_clazz.Get() != nullptr);
+
+  mirror::Class* class_type = mirror::Class::GetJavaLangClass();
+  mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type);
+  Handle<mirror::ObjectArray<mirror::Class>> param_classes = hs.NewHandle(
+      mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, param_types.size()));
+
+  for (uint32_t i = 0; i < param_types.size(); ++i) {
+    Handle<mirror::Class> param = hs.NewHandle(class_linker->FindClass(
+        soa.Self(), FullyQualifiedType(param_types[i]).c_str(), boot_class_loader));
+    param_classes->Set(i, param.Get());
+  }
+
+  return mirror::MethodType::Create(self, return_clazz, param_classes);
+}
+
+
+TEST_F(MethodTypeTest, IsExactMatch) {
+  ScopedObjectAccess soa(Thread::Current());
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> mt1 = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    Handle<mirror::MethodType> mt2 = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    ASSERT_TRUE(mt1->IsExactMatch(mt2.Get()));
+  }
+
+  // Mismatched return type.
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> mt1 = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    Handle<mirror::MethodType> mt2 = hs.NewHandle(CreateMethodType("Integer", { "Integer" }));
+    ASSERT_FALSE(mt1->IsExactMatch(mt2.Get()));
+  }
+
+  // Mismatched param types.
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> mt1 = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    Handle<mirror::MethodType> mt2 = hs.NewHandle(CreateMethodType("String", { "String" }));
+    ASSERT_FALSE(mt1->IsExactMatch(mt2.Get()));
+  }
+
+  // Wrong number of param types.
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> mt1 = hs.NewHandle(
+        CreateMethodType("String", { "String", "String" }));
+    Handle<mirror::MethodType> mt2 = hs.NewHandle(CreateMethodType("String", { "String" }));
+    ASSERT_FALSE(mt1->IsExactMatch(mt2.Get()));
+  }
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 0f5cbb2..ad7558c 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -31,6 +31,7 @@
 #include "lock_word-inl.h"
 #include "monitor.h"
 #include "object_array-inl.h"
+#include "obj_ptr-inl.h"
 #include "read_barrier-inl.h"
 #include "reference.h"
 #include "runtime.h"
@@ -281,7 +282,7 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags>
-inline bool Object::InstanceOf(Class* klass) {
+inline bool Object::InstanceOf(ObjPtr<Class> klass) {
   DCHECK(klass != nullptr);
   DCHECK(GetClass<kVerifyNone>() != nullptr);
   return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
@@ -509,7 +510,7 @@
         template GetObjectSize<kNewFlags, kReadBarrierOption>();
   }
   DCHECK_GE(result, sizeof(Object))
-      << " class=" << PrettyTypeOf(GetClass<kNewFlags, kReadBarrierOption>());
+      << " class=" << PrettyClass(GetClass<kNewFlags, kReadBarrierOption>());
   return result;
 }
 
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 262cb57..10faf60 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -20,6 +20,7 @@
 #include "base/casts.h"
 #include "base/enums.h"
 #include "globals.h"
+#include "obj_ptr.h"
 #include "object_reference.h"
 #include "offsets.h"
 #include "verify_object.h"
@@ -120,7 +121,7 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool VerifierInstanceOf(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  ALWAYS_INLINE bool InstanceOf(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+  ALWAYS_INLINE bool InstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
            ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index afd6115..40ee3a2 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -35,9 +35,10 @@
 #include "gc/heap.h"
 #include "handle_scope-inl.h"
 #include "iftable-inl.h"
+#include "obj_ptr.h"
 #include "object-inl.h"
 #include "object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "string-inl.h"
 
 namespace art {
@@ -364,7 +365,7 @@
   const DexFile* dex_file = GetFirstDexFile(class_loader);
 
   StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<ClassLoader*>(class_loader)));
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<ClassLoader>(class_loader)));
   Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader);
   ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize);
   const DexFile::TypeId* klass_type_id = dex_file->FindTypeId("LStaticsFromCode;");
@@ -494,8 +495,8 @@
   jobject jclass_loader_1 = LoadDex("ProtoCompare");
   jobject jclass_loader_2 = LoadDex("ProtoCompare2");
   StackHandleScope<4> hs(soa.Self());
-  Handle<ClassLoader> class_loader_1(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader_1)));
-  Handle<ClassLoader> class_loader_2(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader_2)));
+  Handle<ClassLoader> class_loader_1(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader_1)));
+  Handle<ClassLoader> class_loader_2(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader_2)));
 
   Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1);
   ASSERT_TRUE(klass1 != nullptr);
@@ -537,7 +538,7 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   StackHandleScope<3> hs(soa.Self());
-  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader)));
+  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader)));
 
   Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
   Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
@@ -574,7 +575,7 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   StackHandleScope<1> hs(soa.Self());
-  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader)));
+  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader)));
   Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
   Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
 
@@ -612,7 +613,7 @@
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("XandY");
   StackHandleScope<1> hs(soa.Self());
-  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader*>(jclass_loader)));
+  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader)));
   Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
   Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
   ASSERT_TRUE(X != nullptr);
@@ -738,5 +739,62 @@
   EXPECT_NE(hash_code, 0);
 }
 
+TEST_F(ObjectTest, ObjectPointer) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader = LoadDex("XandY");
+  StackHandleScope<2> hs(soa.Self());
+  ObjPtr<mirror::Object, /*kPoison*/ true> null_ptr;
+  EXPECT_TRUE(null_ptr.IsNull());
+  EXPECT_TRUE(null_ptr.IsValid());
+  EXPECT_TRUE(null_ptr.Decode() == nullptr);
+  EXPECT_TRUE(null_ptr == nullptr);
+  EXPECT_TRUE(null_ptr == null_ptr);
+  EXPECT_FALSE(null_ptr != null_ptr);
+  EXPECT_FALSE(null_ptr != nullptr);
+  null_ptr.AssertValid();
+  Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader)));
+  Handle<mirror::Class> h_X(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LX;", class_loader)));
+  ObjPtr<Class, /*kPoison*/ true> X(h_X.Get());
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(X.IsValid());
+  EXPECT_TRUE(X.Decode() != nullptr);
+  EXPECT_EQ(h_X.Get(), X.Decode());
+  // FindClass may cause thread suspension, it should invalidate X.
+  ObjPtr<Class, /*kPoison*/ true> Y(class_linker_->FindClass(soa.Self(), "LY;", class_loader));
+  EXPECT_TRUE(!Y.IsNull());
+  EXPECT_TRUE(Y.IsValid());
+  EXPECT_TRUE(Y.Decode() != nullptr);
+
+  // Should IsNull be safe to call on null ObjPtr? I'll allow it for now.
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(!X.IsValid());
+  // Make X valid again by copying out of handle.
+  X.Assign(h_X.Get());
+  EXPECT_TRUE(!X.IsNull());
+  EXPECT_TRUE(X.IsValid());
+  EXPECT_EQ(h_X.Get(), X.Decode());
+
+  // Allow thread suspension to invalidate Y.
+  soa.Self()->AllowThreadSuspension();
+  EXPECT_TRUE(!Y.IsNull());
+  EXPECT_TRUE(!Y.IsValid());
+
+  // Test unpoisoned.
+  ObjPtr<mirror::Object, /*kPoison*/ false> unpoisoned;
+  EXPECT_TRUE(unpoisoned.IsNull());
+  EXPECT_TRUE(unpoisoned.IsValid());
+  EXPECT_TRUE(unpoisoned.Decode() == nullptr);
+  EXPECT_TRUE(unpoisoned == nullptr);
+  EXPECT_TRUE(unpoisoned == unpoisoned);
+  EXPECT_FALSE(unpoisoned != unpoisoned);
+  EXPECT_FALSE(unpoisoned != nullptr);
+
+  unpoisoned = h_X.Get();
+  EXPECT_FALSE(unpoisoned.IsNull());
+  EXPECT_TRUE(unpoisoned == h_X.Get());
+  EXPECT_EQ(unpoisoned.Decode(), h_X.Get());
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index fd7a125..dd32df6 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -67,6 +67,7 @@
 
 // Set by the verifier for a method that could not be verified to follow structured locking.
 static constexpr uint32_t kAccMustCountLocks =        0x02000000;  // method (runtime)
+static constexpr uint32_t kAccIntrinsic  =            0x80000000;  // method (runtime)
 
 // Special runtime-only flags.
 // Interface and all its super-interfaces with default methods have been recursively initialized.
@@ -76,6 +77,9 @@
 // class/ancestor overrides finalize()
 static constexpr uint32_t kAccClassIsFinalizable        = 0x80000000;
 
+static constexpr uint32_t kAccFlagsNotUsedByIntrinsic   = 0x007FFFFF;
+static constexpr uint32_t kAccMaxIntrinsic              = 0xFF;
+
 // Valid (meaningful) bits for a field.
 static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected |
     kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 22cc197..3bc1b06 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -30,7 +30,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "thread_list.h"
 #include "verifier/method_verifier.h"
@@ -457,7 +457,7 @@
   if (!Runtime::Current()->IsStarted() || VLOG_IS_ON(monitor)) {
     std::ostringstream ss;
     self->Dump(ss);
-    LOG(Runtime::Current()->IsStarted() ? INFO : ERROR)
+    LOG(Runtime::Current()->IsStarted() ? ::android::base::INFO : ::android::base::ERROR)
         << self->GetException()->Dump() << "\n" << ss.str();
   }
   va_end(args);
diff --git a/runtime/monitor_pool_test.cc b/runtime/monitor_pool_test.cc
index e1837f5..a111c6c 100644
--- a/runtime/monitor_pool_test.cc
+++ b/runtime/monitor_pool_test.cc
@@ -17,7 +17,7 @@
 #include "monitor_pool.h"
 
 #include "common_runtime_test.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index ac6a4f3..4ee46dc 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -27,7 +27,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/string-inl.h"  // Strings are easiest to allocate
 #include "object_lock.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_pool.h"
 
 namespace art {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 384de34..0677d5b 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -34,7 +34,7 @@
 #include "oat_file_manager.h"
 #include "os.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "utils.h"
@@ -217,7 +217,7 @@
   bool all_deleted = true;
   {
     ScopedObjectAccess soa(env);
-    mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
+    ObjPtr<mirror::Object> dex_files_object = soa.Decode<mirror::Object>(cookie);
     mirror::LongArray* long_dex_files = dex_files_object->AsLongArray();
     // Delete dex files associated with this dalvik.system.DexFile since there should not be running
     // code using it. dex_files is a vector due to multidex.
@@ -277,7 +277,7 @@
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader(
-          hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
+          hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
       class_linker->RegisterDexFile(*dex_file, class_loader.Get());
       mirror::Class* result = class_linker->DefineClass(soa.Self(),
                                                         descriptor.c_str(),
@@ -287,7 +287,7 @@
                                                         *dex_class_def);
       // Add the used dex file. This only required for the DexFile.loadClass API since normal
       // class loaders already keep their dex files live.
-      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object*>(dexFile),
+      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile).Decode(),
                                                  class_loader.Get());
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
index 94933bc..fdced21 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -24,7 +24,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "oat_file.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 
 namespace art {
@@ -148,7 +148,7 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     StackHandleScope<1> handle_scope(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        handle_scope.NewHandle(soa.Decode<mirror::ClassLoader*>(loader)));
+        handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
     class_linker->RegisterDexFile(*dex_file, class_loader.Get());
     mirror::Class* result = class_linker->DefineClass(
         soa.Self(), class_descriptor, hash, class_loader, *dex_file, *dex_class_def);
@@ -157,7 +157,7 @@
       // InMemoryClassLoader/DexData instance now that a class has
       // been loaded.
       class_linker->InsertDexFileInToClassLoader(
-          soa.Decode<mirror::Object*>(dexData), class_loader.Get());
+          soa.Decode<mirror::Object>(dexData).Decode(), class_loader.Get());
       return soa.AddLocalReference<jclass>(result);
     }
   }
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 8f108fa..73c4664 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -36,7 +36,7 @@
 #include "mirror/class.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "trace.h"
 #include "well_known_classes.h"
 
@@ -240,8 +240,8 @@
   ScopedObjectAccess soa(env);
   LOG(INFO) << "--- reference table dump ---";
 
-  soa.Env()->DumpReferenceTables(LOG(INFO));
-  soa.Vm()->DumpReferenceTables(LOG(INFO));
+  soa.Env()->DumpReferenceTables(LOG_STREAM(INFO));
+  soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO));
 
   LOG(INFO) << "---";
 }
@@ -259,11 +259,11 @@
   ScopedObjectAccess soa(env);
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   // Caller's responsibility to do GC if desired.
-  mirror::Class* c = soa.Decode<mirror::Class*>(javaClass);
+  ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(javaClass);
   if (c == nullptr) {
     return 0;
   }
-  std::vector<mirror::Class*> classes {c};
+  std::vector<mirror::Class*> classes {c.Decode()};
   uint64_t count = 0;
   heap->CountInstances(classes, countAssignable, &count);
   return count;
@@ -274,7 +274,8 @@
   ScopedObjectAccess soa(env);
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   // Caller's responsibility to do GC if desired.
-  auto* decoded_classes = soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaClasses);
+  ObjPtr<mirror::ObjectArray<mirror::Class>> decoded_classes =
+      soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses);
   if (decoded_classes == nullptr) {
     return nullptr;
   }
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index d88c9d4..c7fb44ec 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -46,8 +46,8 @@
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
 #include "runtime.h"
-#include "scoped_fast_native_object_access.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "thread_list.h"
 
@@ -74,7 +74,7 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
+  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Decode();
   if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException("element class == null");
     return nullptr;
@@ -99,7 +99,7 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
+  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Decode();
   if (UNLIKELY(element_class == nullptr)) {
     ThrowNullPointerException("element class == null");
     return nullptr;
@@ -122,12 +122,12 @@
     return 0;
   }
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
+  ObjPtr<mirror::Array> array = soa.Decode<mirror::Array>(javaArray);
   if (!array->IsArrayInstance()) {
     ThrowIllegalArgumentException("not an array");
     return 0;
   }
-  if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
+  if (Runtime::Current()->GetHeap()->IsMovableObject(array.Decode())) {
     ThrowRuntimeException("Trying to get address of movable array object");
     return 0;
   }
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 9da40b9..0dd8cdd 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -22,8 +22,8 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread_list.h"
 
 namespace art {
@@ -31,7 +31,7 @@
 static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject peer)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   jobject trace = nullptr;
-  if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
+  if (soa.Decode<mirror::Object>(peer) == soa.Self()->GetPeer()) {
     trace = soa.Self()->CreateInternalStackTrace<false>(soa);
   } else {
     // Suspend thread to build stack trace.
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index fe3cbe7..a78909b 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -24,7 +24,7 @@
 #include "jit/jit.h"
 #include "jni_internal.h"
 #include "JNIHelp.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "thread-inl.h"
 #include "trace.h"
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index af9b68f..34bd57b 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -33,9 +33,10 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/string-inl.h"
+#include "obj_ptr-inl.h"
 #include "reflection.h"
-#include "scoped_thread_state_change.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_thread_state_change-inl.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "utf.h"
@@ -43,10 +44,10 @@
 
 namespace art {
 
-ALWAYS_INLINE static inline mirror::Class* DecodeClass(
+ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
     const ScopedFastNativeObjectAccess& soa, jobject java_class)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+  ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(java_class);
   DCHECK(c != nullptr);
   DCHECK(c->IsClass());
   // TODO: we could EnsureInitialized here, rather than on every reflective get/set or invoke .
@@ -75,16 +76,19 @@
 
   std::string descriptor(DotToDescriptor(name.c_str()));
   StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> c(
       hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
   if (c.Get() == nullptr) {
     ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
     env->ExceptionClear();
-    jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
-                                                                  WellKnownClasses::java_lang_ClassNotFoundException_init,
-                                                                  javaName, cause.get()));
+    jthrowable cnfe = reinterpret_cast<jthrowable>(
+        env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
+                       WellKnownClasses::java_lang_ClassNotFoundException_init,
+                       javaName,
+                       cause.get()));
     if (cnfe != nullptr) {
       // Make sure allocation didn't fail with an OOME.
       env->Throw(cnfe);
@@ -100,18 +104,18 @@
 static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  mirror::Class* const c = DecodeClass(soa, javaThis);
+  ObjPtr<mirror::Class> c = DecodeClass(soa, javaThis);
   return soa.AddLocalReference<jstring>(mirror::Class::ComputeName(hs.NewHandle(c)));
 }
 
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Class* c = DecodeClass(soa, javaThis);
+  ObjPtr<mirror::Class> c = DecodeClass(soa, javaThis);
   return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
 }
 
 static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
-    Thread* self, mirror::Class* klass, bool public_only, bool force_resolve)
+    Thread* self, ObjPtr<mirror::Class> klass, bool public_only, bool force_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
@@ -191,8 +195,8 @@
 // Performs a binary search through an array of fields, TODO: Is this fast enough if we don't use
 // the dex cache for lookups? I think CompareModifiedUtf8ToUtf16AsCodePointValues should be fairly
 // fast.
-ALWAYS_INLINE static inline ArtField* FindFieldByName(
-    Thread* self ATTRIBUTE_UNUSED, mirror::String* name, LengthPrefixedArray<ArtField>* fields)
+ALWAYS_INLINE static inline ArtField* FindFieldByName(ObjPtr<mirror::String> name,
+                                                      LengthPrefixedArray<ArtField>* fields)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (fields == nullptr) {
     return nullptr;
@@ -236,14 +240,15 @@
   return nullptr;
 }
 
-ALWAYS_INLINE static inline mirror::Field* GetDeclaredField(
-    Thread* self, mirror::Class* c, mirror::String* name)
+ALWAYS_INLINE static inline mirror::Field* GetDeclaredField(Thread* self,
+                                                            ObjPtr<mirror::Class> c,
+                                                            ObjPtr<mirror::String> name)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ArtField* art_field = FindFieldByName(self, name, c->GetIFieldsPtr());
+  ArtField* art_field = FindFieldByName(name, c->GetIFieldsPtr());
   if (art_field != nullptr) {
     return mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, art_field, true);
   }
-  art_field = FindFieldByName(self, name, c->GetSFieldsPtr());
+  art_field = FindFieldByName(name, c->GetSFieldsPtr());
   if (art_field != nullptr) {
     return mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, art_field, true);
   }
@@ -251,7 +256,7 @@
 }
 
 static mirror::Field* GetPublicFieldRecursive(
-    Thread* self, mirror::Class* clazz, mirror::String* name)
+    Thread* self, ObjPtr<mirror::Class> clazz, ObjPtr<mirror::String> name)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(clazz != nullptr);
   DCHECK(name != nullptr);
@@ -301,7 +306,7 @@
 
 static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstring name) {
   ScopedFastNativeObjectAccess soa(env);
-  auto* name_string = soa.Decode<mirror::String*>(name);
+  auto name_string = soa.Decode<mirror::String>(name);
   if (UNLIKELY(name_string == nullptr)) {
     ThrowNullPointerException("name == null");
     return nullptr;
@@ -312,16 +317,18 @@
 
 static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) {
   ScopedFastNativeObjectAccess soa(env);
-  auto* name_string = soa.Decode<mirror::String*>(name);
-  if (name_string == nullptr) {
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::String> h_string = hs.NewHandle(soa.Decode<mirror::String>(name));
+  if (h_string.Get() == nullptr) {
     ThrowNullPointerException("name == null");
     return nullptr;
   }
-  auto* klass = DecodeClass(soa, javaThis);
-  mirror::Field* result = GetDeclaredField(soa.Self(), klass, name_string);
-  if (result == nullptr) {
-    std::string name_str = name_string->ToModifiedUtf8();
-    if (name_str == "value" && klass->IsStringClass()) {
+  Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
+  Handle<mirror::Field> result =
+      hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
+  if (result.Get() == nullptr) {
+    std::string name_str = h_string->ToModifiedUtf8();
+    if (name_str == "value" && h_klass->IsStringClass()) {
       // We log the error for this specific case, as the user might just swallow the exception.
       // This helps diagnose crashes when applications rely on the String#value field being
       // there.
@@ -332,11 +339,11 @@
     }
     // We may have a pending exception if we failed to resolve.
     if (!soa.Self()->IsExceptionPending()) {
-      ThrowNoSuchFieldException(DecodeClass(soa, javaThis), name_str.c_str());
+      ThrowNoSuchFieldException(h_klass.Get(), name_str.c_str());
     }
     return nullptr;
   }
-  return soa.AddLocalReference<jobject>(result);
+  return soa.AddLocalReference<jobject>(result.Get());
 }
 
 static jobject Class_getDeclaredConstructorInternal(
@@ -344,11 +351,11 @@
   ScopedFastNativeObjectAccess soa(env);
   DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
   DCHECK(!Runtime::Current()->IsActiveTransaction());
-  mirror::Constructor* result = mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize,
-                                                                              false>(
+  ObjPtr<mirror::Constructor> result =
+      mirror::Class::GetDeclaredConstructorInternal<kRuntimePointerSize, false>(
       soa.Self(),
-      DecodeClass(soa, javaThis),
-      soa.Decode<mirror::ObjectArray<mirror::Class>*>(args));
+      DecodeClass(soa, javaThis).Decode(),
+      soa.Decode<mirror::ObjectArray<mirror::Class>>(args).Decode());
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -398,9 +405,9 @@
   DCHECK(!Runtime::Current()->IsActiveTransaction());
   mirror::Method* result = mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>(
       soa.Self(),
-      DecodeClass(soa, javaThis),
-      soa.Decode<mirror::String*>(name),
-      soa.Decode<mirror::ObjectArray<mirror::Class>*>(args));
+      DecodeClass(soa, javaThis).Decode(),
+      soa.Decode<mirror::String>(name).Decode(),
+      soa.Decode<mirror::ObjectArray<mirror::Class>>(args).Decode());
   return soa.AddLocalReference<jobject>(result);
 }
 
@@ -453,7 +460,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
-  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationClass)));
+  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class>(annotationClass)));
   return soa.AddLocalReference<jobject>(
       annotations::GetAnnotationForClass(klass, annotation_class));
 }
@@ -464,10 +471,12 @@
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     // Return an empty array instead of a null pointer.
-    mirror::Class* annotation_array_class =
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
+    ObjPtr<mirror::Class>  annotation_array_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
     mirror::ObjectArray<mirror::Object>* empty_array =
-        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
+        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(),
+                                                   annotation_array_class.Decode(),
+                                                   0);
     return soa.AddLocalReference<jobjectArray>(empty_array);
   }
   return soa.AddLocalReference<jobjectArray>(annotations::GetAnnotationsForClass(klass));
@@ -519,8 +528,8 @@
   }
   mirror::Object* method = annotations::GetEnclosingMethod(klass);
   if (method != nullptr) {
-    if (method->GetClass() ==
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Constructor)) {
+    if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Constructor) ==
+        method->GetClass()) {
       return soa.AddLocalReference<jobject>(method);
     }
   }
@@ -536,8 +545,8 @@
   }
   mirror::Object* method = annotations::GetEnclosingMethod(klass);
   if (method != nullptr) {
-    if (method->GetClass() ==
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Method)) {
+    if (soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Method) ==
+        method->GetClass()) {
       return soa.AddLocalReference<jobject>(method);
     }
   }
@@ -598,7 +607,7 @@
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return false;
   }
-  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
   return annotations::IsClassAnnotationPresent(klass, annotation_class);
 }
 
@@ -669,7 +678,10 @@
       caller.Assign(GetCallingClass(soa.Self(), 1));
     }
     if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(
-        receiver.Get(), declaring_class, constructor->GetAccessFlags(), caller.Get()))) {
+        MakeObjPtr(receiver.Get()),
+        MakeObjPtr(declaring_class),
+        constructor->GetAccessFlags(),
+        MakeObjPtr(caller.Get())))) {
       soa.Self()->ThrowNewExceptionF(
           "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
           PrettyMethod(constructor).c_str(), PrettyClass(caller.Get()).c_str());
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index f0140a3..5efafe7 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -21,14 +21,14 @@
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
 
 static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   // Should only be called while holding the lock on the dex cache.
   DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId());
   const DexFile* dex_file = dex_cache->GetDexFile();
@@ -51,14 +51,14 @@
 
 static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(type_index));
 }
 
 static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds());
   return soa.AddLocalReference<jobject>(dex_cache->GetResolvedString(string_index));
 }
@@ -66,17 +66,17 @@
 static void DexCache_setResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index,
                                      jobject type) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
-  dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class*>(type));
+  dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class>(type).Decode());
 }
 
 static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index,
                                        jobject string) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+  ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
   CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds());
-  dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String*>(string));
+  dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String>(string).Decode());
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 2a36059..6493865 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -18,39 +18,35 @@
 
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 
 
 namespace art {
 
 static jobject Object_internalClone(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_this);
   return soa.AddLocalReference<jobject>(o->Clone(soa.Self()));
 }
 
 static void Object_notify(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
-  o->Notify(soa.Self());
+  soa.Decode<mirror::Object>(java_this)->Notify(soa.Self());
 }
 
 static void Object_notifyAll(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
-  o->NotifyAll(soa.Self());
+  soa.Decode<mirror::Object>(java_this)->NotifyAll(soa.Self());
 }
 
 static void Object_wait(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
-  o->Wait(soa.Self());
+  soa.Decode<mirror::Object>(java_this)->Wait(soa.Self());
 }
 
 static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
-  o->Wait(soa.Self(), ms, ns);
+  soa.Decode<mirror::Object>(java_this)->Wait(soa.Self(), ms, ns);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index aa64b79..b3a967d 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -22,8 +22,8 @@
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
 #include "mirror/string-inl.h"
-#include "scoped_fast_native_object_access.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "verify_object-inl.h"
 
@@ -31,7 +31,7 @@
 
 static jchar String_charAt(JNIEnv* env, jobject java_this, jint index) {
   ScopedFastNativeObjectAccess soa(env);
-  return soa.Decode<mirror::String*>(java_this)->CharAt(index);
+  return soa.Decode<mirror::String>(java_this)->CharAt(index);
 }
 
 static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) {
@@ -40,7 +40,8 @@
     ThrowNullPointerException("rhs == null");
     return -1;
   } else {
-    return soa.Decode<mirror::String*>(java_this)->CompareTo(soa.Decode<mirror::String*>(java_rhs));
+    return soa.Decode<mirror::String>(java_this)->CompareTo(
+        soa.Decode<mirror::String>(java_rhs).Decode());
   }
 }
 
@@ -51,8 +52,8 @@
     return nullptr;
   }
   StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String*>(java_this)));
-  Handle<mirror::String> string_arg(hs.NewHandle(soa.Decode<mirror::String*>(java_string_arg)));
+  Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String>(java_this)));
+  Handle<mirror::String> string_arg(hs.NewHandle(soa.Decode<mirror::String>(java_string_arg)));
   int32_t length_this = string_this->GetLength();
   int32_t length_arg = string_arg->GetLength();
   if (length_arg > 0 && length_this > 0) {
@@ -67,13 +68,13 @@
   ScopedFastNativeObjectAccess soa(env);
   // This method does not handle supplementary characters. They're dealt with in managed code.
   DCHECK_LE(ch, 0xffff);
-  return soa.Decode<mirror::String*>(java_this)->FastIndexOf(ch, start);
+  return soa.Decode<mirror::String>(java_this)->FastIndexOf(ch, start);
 }
 
 static jstring String_fastSubstring(JNIEnv* env, jobject java_this, jint start, jint length) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String*>(java_this)));
+  Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String>(java_this)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
   mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), length, string_this,
                                                                  start, allocator_type);
@@ -84,25 +85,24 @@
                                    jcharArray buffer, jint index) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray*>(buffer)));
-  soa.Decode<mirror::String*>(java_this)->GetChars(start, end, char_array, index);
+  Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray>(buffer)));
+  soa.Decode<mirror::String>(java_this)->GetChars(start, end, char_array, index);
 }
 
 static jstring String_intern(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::String* s = soa.Decode<mirror::String*>(java_this);
-  mirror::String* result = s->Intern();
+  ObjPtr<mirror::String> result = soa.Decode<mirror::String>(java_this)->Intern();
   return soa.AddLocalReference<jstring>(result);
 }
 
 static void String_setCharAt(JNIEnv* env, jobject java_this, jint index, jchar c) {
   ScopedFastNativeObjectAccess soa(env);
-  soa.Decode<mirror::String*>(java_this)->SetCharAt(index, c);
+  soa.Decode<mirror::String>(java_this)->SetCharAt(index, c);
 }
 
 static jcharArray String_toCharArray(JNIEnv* env, jobject java_this) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::String* s = soa.Decode<mirror::String*>(java_this);
+  ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_this);
   return soa.AddLocalReference<jcharArray>(s->ToCharArray(soa.Self()));
 }
 
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
index 5a219ef..119f2b8 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -20,8 +20,8 @@
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
-#include "scoped_fast_native_object_access.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 
@@ -35,7 +35,7 @@
     return nullptr;
   }
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray*>(java_data)));
+  Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data)));
   int32_t data_size = byte_array->GetLength();
   if ((offset | byte_count) < 0 || byte_count > data_size - offset) {
     soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
@@ -56,7 +56,7 @@
   DCHECK(java_data != nullptr);
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray*>(java_data)));
+  Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray>(java_data)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
   mirror::String* result = mirror::String::AllocFromCharArray<true>(soa.Self(), char_count,
                                                                     char_array, offset,
@@ -71,7 +71,7 @@
     return nullptr;
   }
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(to_copy)));
+  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(to_copy)));
   gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
   mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), string->GetLength(),
                                                                  string, 0, allocator_type);
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 1b399aa..8b9d0c7 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -24,7 +24,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 
 namespace art {
 
@@ -60,14 +60,14 @@
   }
 
   // Make sure source and destination are both arrays.
-  mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
+  ObjPtr<mirror::Object> srcObject = soa.Decode<mirror::Object>(javaSrc);
   if (UNLIKELY(!srcObject->IsArrayInstance())) {
-    ThrowArrayStoreException_NotAnArray("source", srcObject);
+    ThrowArrayStoreException_NotAnArray("source", srcObject.Decode());
     return;
   }
-  mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst);
+  ObjPtr<mirror::Object> dstObject = soa.Decode<mirror::Object>(javaDst);
   if (UNLIKELY(!dstObject->IsArrayInstance())) {
-    ThrowArrayStoreException_NotAnArray("destination", dstObject);
+    ThrowArrayStoreException_NotAnArray("destination", dstObject.Decode());
     return;
   }
   mirror::Array* srcArray = srcObject->AsArray();
@@ -164,8 +164,8 @@
 inline void System_arraycopyTUnchecked(JNIEnv* env, jobject javaSrc, jint srcPos,
                                        jobject javaDst, jint dstPos, jint count) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
-  mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst);
+  ObjPtr<mirror::Object> srcObject = soa.Decode<mirror::Object>(javaSrc);
+  ObjPtr<mirror::Object> dstObject = soa.Decode<mirror::Object>(javaDst);
   DCHECK(dstObject != nullptr);
   mirror::Array* srcArray = srcObject->AsArray();
   mirror::Array* dstArray = dstObject->AsArray();
@@ -228,7 +228,7 @@
     return 0;
   }
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* o = soa.Decode<mirror::Object*>(javaObject);
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(javaObject);
   return static_cast<jint>(o->IdentityHashCode());
 }
 
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index a742e81..0635261 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -20,8 +20,8 @@
 #include "jni_internal.h"
 #include "monitor.h"
 #include "mirror/object.h"
-#include "scoped_fast_native_object_access.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -109,14 +109,14 @@
 
 static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) {
   ScopedObjectAccess soa(env);
-  mirror::Object* object = soa.Decode<mirror::Object*>(java_object);
+  ObjPtr<mirror::Object> object = soa.Decode<mirror::Object>(java_object);
   if (object == nullptr) {
     ThrowNullPointerException("object == null");
     return JNI_FALSE;
   }
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = Thread::FromManagedThread(soa, java_thread);
-  return thread->HoldsLock(object);
+  return thread->HoldsLock(object.Decode());
 }
 
 static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
@@ -132,7 +132,7 @@
   ScopedUtfChars name(env, java_name);
   {
     ScopedObjectAccess soa(env);
-    if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
+    if (soa.Decode<mirror::Object>(peer) == soa.Self()->GetPeer()) {
       soa.Self()->SetThreadName(name.c_str());
       return;
     }
@@ -172,8 +172,8 @@
 
 static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
-  Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
+  ObjPtr<mirror::Object> lock = soa.Decode<mirror::Object>(java_lock);
+  Monitor::Wait(Thread::Current(), lock.Decode(), ms, ns, true, kSleeping);
 }
 
 /*
diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc
index cb8a869..ff3e044 100644
--- a/runtime/native/java_lang_Throwable.cc
+++ b/runtime/native/java_lang_Throwable.cc
@@ -17,7 +17,7 @@
 #include "java_lang_Throwable.h"
 
 #include "jni_internal.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 6f735aa..0694c4d 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -20,7 +20,7 @@
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "ScopedUtfChars.h"
 #include "zip_archive.h"
 
@@ -29,7 +29,7 @@
 static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader,
                                             jstring javaName) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
+  ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(javaLoader);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == nullptr) {
     return nullptr;
@@ -37,7 +37,10 @@
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
   std::string descriptor(DotToDescriptor(name.c_str()));
   const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
-  mirror::Class* c = cl->LookupClass(soa.Self(), descriptor.c_str(), descriptor_hash, loader);
+  mirror::Class* c = cl->LookupClass(soa.Self(),
+                                     descriptor.c_str(),
+                                     descriptor_hash,
+                                     loader.Decode());
   if (c != nullptr && c->IsResolved()) {
     return soa.AddLocalReference<jclass>(c);
   }
diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc
index 0532c35..08bcc38 100644
--- a/runtime/native/java_lang_ref_FinalizerReference.cc
+++ b/runtime/native/java_lang_ref_FinalizerReference.cc
@@ -21,14 +21,15 @@
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 
 namespace art {
 
 static jboolean FinalizerReference_makeCircularListIfUnenqueued(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::FinalizerReference* const ref = soa.Decode<mirror::FinalizerReference*>(javaThis);
-  return Runtime::Current()->GetHeap()->GetReferenceProcessor()->MakeCircularListIfUnenqueued(ref);
+  ObjPtr<mirror::FinalizerReference> ref = soa.Decode<mirror::FinalizerReference>(javaThis);
+  return Runtime::Current()->GetHeap()->GetReferenceProcessor()->MakeCircularListIfUnenqueued(
+      ref.Decode());
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index d232059..9a088ed 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -21,15 +21,15 @@
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 
 namespace art {
 
 static jobject Reference_getReferent(JNIEnv* env, jobject javaThis) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Reference* const ref = soa.Decode<mirror::Reference*>(javaThis);
+  ObjPtr<mirror::Reference> ref = soa.Decode<mirror::Reference>(javaThis);
   mirror::Object* const referent =
-      Runtime::Current()->GetHeap()->GetReferenceProcessor()->GetReferent(soa.Self(), ref);
+      Runtime::Current()->GetHeap()->GetReferenceProcessor()->GetReferent(soa.Self(), ref.Decode());
   return soa.AddLocalReference<jobject>(referent);
 }
 
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.cc b/runtime/native/java_lang_reflect_AbstractMethod.cc
deleted file mode 100644
index 254f8db..0000000
--- a/runtime/native/java_lang_reflect_AbstractMethod.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "java_lang_reflect_AbstractMethod.h"
-
-#include "art_method-inl.h"
-#include "dex_file_annotations.h"
-#include "jni_internal.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "reflection.h"
-#include "scoped_fast_native_object_access.h"
-#include "well_known_classes.h"
-
-namespace art {
-
-static jobjectArray AbstractMethod_getDeclaredAnnotations(JNIEnv* env, jobject javaMethod) {
-  ScopedFastNativeObjectAccess soa(env);
-  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
-    // Return an empty array instead of a null pointer.
-    mirror::Class* annotation_array_class =
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
-    mirror::ObjectArray<mirror::Object>* empty_array =
-        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
-    return soa.AddLocalReference<jobjectArray>(empty_array);
-  }
-  return soa.AddLocalReference<jobjectArray>(annotations::GetAnnotationsForMethod(method));
-}
-
-static jobject AbstractMethod_getAnnotationNative(JNIEnv* env,
-                                                  jobject javaMethod,
-                                                  jclass annotationType) {
-  ScopedFastNativeObjectAccess soa(env);
-  StackHandleScope<1> hs(soa.Self());
-  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->IsProxyMethod()) {
-    return nullptr;
-  } else {
-    Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
-    return soa.AddLocalReference<jobject>(annotations::GetAnnotationForMethod(method, klass));
-  }
-}
-
-static jobjectArray AbstractMethod_getSignatureAnnotation(JNIEnv* env, jobject javaMethod) {
-  ScopedFastNativeObjectAccess soa(env);
-  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
-    return nullptr;
-  }
-  StackHandleScope<1> hs(soa.Self());
-  return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForMethod(method));
-}
-
-
-static jobjectArray AbstractMethod_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) {
-  ScopedFastNativeObjectAccess soa(env);
-  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->IsProxyMethod()) {
-    return nullptr;
-  } else {
-    return soa.AddLocalReference<jobjectArray>(annotations::GetParameterAnnotations(method));
-  }
-}
-
-static jboolean AbstractMethod_isAnnotationPresentNative(JNIEnv* env,
-                                                         jobject javaMethod,
-                                                         jclass annotationType) {
-  ScopedFastNativeObjectAccess soa(env);
-  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
-  if (method->GetDeclaringClass()->IsProxyClass()) {
-    return false;
-  }
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
-  return annotations::IsMethodAnnotationPresent(method, klass);
-}
-
-static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(AbstractMethod, getAnnotationNative,
-                "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
-  NATIVE_METHOD(AbstractMethod, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"),
-  NATIVE_METHOD(AbstractMethod, getParameterAnnotationsNative,
-                "!()[[Ljava/lang/annotation/Annotation;"),
-  NATIVE_METHOD(AbstractMethod, getSignatureAnnotation, "!()[Ljava/lang/String;"),
-  NATIVE_METHOD(AbstractMethod, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"),
-};
-
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env) {
-  REGISTER_NATIVE_METHODS("java/lang/reflect/AbstractMethod");
-}
-
-}  // namespace art
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index beb953b..3718ce8 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -22,7 +22,7 @@
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "handle_scope-inl.h"
 
 namespace art {
@@ -32,15 +32,15 @@
   ScopedFastNativeObjectAccess soa(env);
   DCHECK(javaElementClass != nullptr);
   StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::Class> element_class(hs.NewHandle(soa.Decode<mirror::Class*>(javaElementClass)));
+  Handle<mirror::Class> element_class(hs.NewHandle(soa.Decode<mirror::Class>(javaElementClass)));
   DCHECK(element_class->IsClass());
   DCHECK(javaDimArray != nullptr);
-  mirror::Object* dimensions_obj = soa.Decode<mirror::Object*>(javaDimArray);
+  ObjPtr<mirror::Object> dimensions_obj = soa.Decode<mirror::Object>(javaDimArray);
   DCHECK(dimensions_obj->IsArrayInstance());
   DCHECK_EQ(dimensions_obj->GetClass()->GetComponentType()->GetPrimitiveType(),
             Primitive::kPrimInt);
   Handle<mirror::IntArray> dimensions_array(
-      hs.NewHandle(down_cast<mirror::IntArray*>(dimensions_obj)));
+      hs.NewHandle(down_cast<mirror::IntArray*>(dimensions_obj.Decode())));
   mirror::Array* new_array = mirror::Array::CreateMultiArray(soa.Self(), element_class,
                                                              dimensions_array);
   return soa.AddLocalReference<jobject>(new_array);
@@ -53,7 +53,7 @@
     ThrowNegativeArraySizeException(length);
     return nullptr;
   }
-  mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
+  mirror::Class* element_class = soa.Decode<mirror::Class>(javaElementClass).Decode();
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
   mirror::Class* array_class = class_linker->FindArrayClass(soa.Self(), &element_class);
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index d001d0c..7de0147 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -26,7 +26,7 @@
 #include "mirror/method.h"
 #include "mirror/object-inl.h"
 #include "reflection.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -60,7 +60,7 @@
  */
 static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Constructor* m = soa.Decode<mirror::Constructor*>(javaMethod);
+  ObjPtr<mirror::Constructor> m = soa.Decode<mirror::Constructor>(javaMethod);
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass()));
   if (UNLIKELY(c->IsAbstract())) {
@@ -73,7 +73,7 @@
   if (!m->IsAccessible() && !c->IsPublic()) {
     // Go 2 frames back, this method is always called from newInstance0, which is called from
     // Constructor.newInstance(Object... args).
-    auto* caller = GetCallingClass(soa.Self(), 2);
+    ObjPtr<mirror::Class> caller = GetCallingClass(soa.Self(), 2);
     // If caller is null, then we called from JNI, just avoid the check since JNI avoids most
     // access checks anyways. TODO: Investigate if this the correct behavior.
     if (caller != nullptr && !caller->CanAccess(c.Get())) {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
new file mode 100644
index 0000000..c7c8008
--- /dev/null
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "java_lang_reflect_Executable.h"
+
+#include "art_method-inl.h"
+#include "dex_file_annotations.h"
+#include "handle.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/method.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "reflection.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+static jobjectArray Executable_getDeclaredAnnotationsNative(JNIEnv* env, jobject javaMethod) {
+  ScopedFastNativeObjectAccess soa(env);
+  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+  if (method->GetDeclaringClass()->IsProxyClass()) {
+    // Return an empty array instead of a null pointer.
+    ObjPtr<mirror::Class> annotation_array_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+    ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
+        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class.Decode(), 0);
+    return soa.AddLocalReference<jobjectArray>(empty_array);
+  }
+  return soa.AddLocalReference<jobjectArray>(annotations::GetAnnotationsForMethod(method));
+}
+
+static jobject Executable_getAnnotationNative(JNIEnv* env,
+                                              jobject javaMethod,
+                                              jclass annotationType) {
+  ScopedFastNativeObjectAccess soa(env);
+  StackHandleScope<1> hs(soa.Self());
+  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+  if (method->IsProxyMethod()) {
+    return nullptr;
+  } else {
+    Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
+    return soa.AddLocalReference<jobject>(annotations::GetAnnotationForMethod(method, klass));
+  }
+}
+
+static jobjectArray Executable_getSignatureAnnotation(JNIEnv* env, jobject javaMethod) {
+  ScopedFastNativeObjectAccess soa(env);
+  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+  if (method->GetDeclaringClass()->IsProxyClass()) {
+    return nullptr;
+  }
+  StackHandleScope<1> hs(soa.Self());
+  return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForMethod(method));
+}
+
+
+static jobjectArray Executable_getParameterAnnotationsNative(JNIEnv* env, jobject javaMethod) {
+  ScopedFastNativeObjectAccess soa(env);
+  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+  if (method->IsProxyMethod()) {
+    return nullptr;
+  } else {
+    return soa.AddLocalReference<jobjectArray>(annotations::GetParameterAnnotations(method));
+  }
+}
+
+static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
+  ScopedFastNativeObjectAccess soa(env);
+  Thread* self = soa.Self();
+  StackHandleScope<8> hs(self);
+
+  Handle<mirror::Method> executable = hs.NewHandle(soa.Decode<mirror::Method>(javaMethod));
+  ArtMethod* art_method = executable.Get()->GetArtMethod();
+  if (art_method->GetDeclaringClass()->IsProxyClass()) {
+    return nullptr;
+  }
+
+  // Find the MethodParameters system annotation.
+  MutableHandle<mirror::ObjectArray<mirror::String>> names =
+      hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr);
+  MutableHandle<mirror::IntArray> access_flags = hs.NewHandle<mirror::IntArray>(nullptr);
+  if (!annotations::GetParametersMetadataForMethod(art_method, &names, &access_flags)) {
+    return nullptr;
+  }
+
+  // Validate the MethodParameters system annotation data.
+  if (UNLIKELY(names.Get() == nullptr || access_flags.Get() == nullptr)) {
+    ThrowIllegalArgumentException(
+        StringPrintf("Missing parameter metadata for names or access flags for %s",
+                     PrettyMethod(art_method).c_str()).c_str());
+    return nullptr;
+  }
+
+  // Check array sizes match each other
+  int32_t names_count = names.Get()->GetLength();
+  int32_t access_flags_count = access_flags.Get()->GetLength();
+  if (names_count != access_flags_count) {
+    ThrowIllegalArgumentException(
+        StringPrintf(
+            "Inconsistent parameter metadata for %s. names length: %d, access flags length: %d",
+            PrettyMethod(art_method).c_str(),
+            names_count,
+            access_flags_count).c_str());
+    return nullptr;
+  }
+
+  // Instantiate a Parameter[] to hold the result.
+  Handle<mirror::Class> parameter_array_class =
+      hs.NewHandle(
+          soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter__array));
+  Handle<mirror::ObjectArray<mirror::Object>> parameter_array =
+      hs.NewHandle(
+          mirror::ObjectArray<mirror::Object>::Alloc(self,
+                                                     parameter_array_class.Get(),
+                                                     names_count));
+  if (UNLIKELY(parameter_array.Get() == nullptr)) {
+    self->AssertPendingException();
+    return nullptr;
+  }
+
+  Handle<mirror::Class> parameter_class =
+      hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter));
+  ArtMethod* parameter_init =
+      soa.DecodeMethod(WellKnownClasses::java_lang_reflect_Parameter_init);
+
+  // Mutable handles used in the loop below to ensure cleanup without scaling the number of
+  // handles by the number of parameters.
+  MutableHandle<mirror::String> name = hs.NewHandle<mirror::String>(nullptr);
+  MutableHandle<mirror::Object> parameter = hs.NewHandle<mirror::Object>(nullptr);
+
+  // Populate the Parameter[] to return.
+  for (int32_t parameter_index = 0; parameter_index < names_count; parameter_index++) {
+    name.Assign(names.Get()->Get(parameter_index));
+    int32_t modifiers = access_flags.Get()->Get(parameter_index);
+
+    // Allocate / initialize the Parameter to add to parameter_array.
+    parameter.Assign(parameter_class->AllocObject(self));
+    if (UNLIKELY(parameter.Get() == nullptr)) {
+      self->AssertPendingOOMException();
+      return nullptr;
+    }
+
+    uint32_t args[5] = { PointerToLowMemUInt32(parameter.Get()),
+                         PointerToLowMemUInt32(name.Get()),
+                         static_cast<uint32_t>(modifiers),
+                         PointerToLowMemUInt32(executable.Get()),
+                         static_cast<uint32_t>(parameter_index)
+    };
+    JValue result;
+    static const char* method_signature = "VLILI";  // return + parameter types
+    parameter_init->Invoke(self, args, sizeof(args), &result, method_signature);
+    if (UNLIKELY(self->IsExceptionPending())) {
+      return nullptr;
+    }
+
+    // Store the Parameter in the Parameter[].
+    parameter_array.Get()->Set(parameter_index, parameter.Get());
+    if (UNLIKELY(self->IsExceptionPending())) {
+      return nullptr;
+    }
+  }
+  return soa.AddLocalReference<jobjectArray>(parameter_array.Get());
+}
+
+static jboolean Executable_isAnnotationPresentNative(JNIEnv* env,
+                                                     jobject javaMethod,
+                                                     jclass annotationType) {
+  ScopedFastNativeObjectAccess soa(env);
+  ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
+  if (method->GetDeclaringClass()->IsProxyClass()) {
+    return false;
+  }
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
+  return annotations::IsMethodAnnotationPresent(method, klass);
+}
+
+static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(Executable, getAnnotationNative,
+                "!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+  NATIVE_METHOD(Executable, getDeclaredAnnotationsNative, "!()[Ljava/lang/annotation/Annotation;"),
+  NATIVE_METHOD(Executable, getParameterAnnotationsNative,
+                "!()[[Ljava/lang/annotation/Annotation;"),
+  NATIVE_METHOD(Executable, getParameters0, "!()[Ljava/lang/reflect/Parameter;"),
+  NATIVE_METHOD(Executable, getSignatureAnnotation, "!()[Ljava/lang/String;"),
+  NATIVE_METHOD(Executable, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"),
+};
+
+void register_java_lang_reflect_Executable(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("java/lang/reflect/Executable");
+}
+
+}  // namespace art
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.h b/runtime/native/java_lang_reflect_Executable.h
similarity index 72%
rename from runtime/native/java_lang_reflect_AbstractMethod.h
rename to runtime/native/java_lang_reflect_Executable.h
index 222e5a0..0cfed62 100644
--- a/runtime/native/java_lang_reflect_AbstractMethod.h
+++ b/runtime/native/java_lang_reflect_Executable.h
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_EXECUTABLE_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_EXECUTABLE_H_
 
 #include <jni.h>
 
 namespace art {
 
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env);
+void register_java_lang_reflect_Executable(JNIEnv* env);
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_EXECUTABLE_H_
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 412445f..2519225 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -25,7 +25,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/field.h"
 #include "reflection-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "utils.h"
 
 namespace art {
@@ -43,9 +43,13 @@
                     PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
     return false;
   }
-  mirror::Class* calling_class = nullptr;
-  if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(),
-                    &calling_class, 1)) {
+  ObjPtr<mirror::Class> calling_class;
+  if (!VerifyAccess(self,
+                    MakeObjPtr(obj),
+                    MakeObjPtr(field->GetDeclaringClass()),
+                    field->GetAccessFlags(),
+                    &calling_class,
+                    1)) {
     ThrowIllegalAccessException(
             StringPrintf("Class %s cannot access %s field %s of class %s",
                 calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
@@ -123,8 +127,8 @@
     *class_or_rcvr = declaringClass;
     return true;
   }
-  *class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
-  if (!VerifyObjectIsClass(*class_or_rcvr, declaringClass)) {
+  *class_or_rcvr = soa.Decode<mirror::Object>(j_rcvr).Decode();
+  if (!VerifyObjectIsClass(MakeObjPtr(*class_or_rcvr), MakeObjPtr(declaringClass))) {
     DCHECK(soa.Self()->IsExceptionPending());
     return false;
   }
@@ -133,7 +137,7 @@
 
 static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field*>(javaField);
+  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Decode();
   mirror::Object* o = nullptr;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     DCHECK(soa.Self()->IsExceptionPending());
@@ -152,14 +156,14 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
-  return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value));
+  return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value).Decode());
 }
 
 template<Primitive::Type kPrimitiveType>
 ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField,
                                                      jobject javaObj) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field*>(javaField);
+  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Decode();
   mirror::Object* o = nullptr;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     DCHECK(soa.Self()->IsExceptionPending());
@@ -303,7 +307,7 @@
 
 static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field*>(javaField);
+  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Decode();
   // Check that the receiver is non-null and an instance of the field's declaring class.
   mirror::Object* o = nullptr;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
@@ -321,9 +325,12 @@
   }
   // We now don't expect suspension unless an exception is thrown.
   // Unbox the value, if necessary.
-  mirror::Object* boxed_value = soa.Decode<mirror::Object*>(javaValue);
+  ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue);
   JValue unboxed_value;
-  if (!UnboxPrimitiveForField(boxed_value, field_type, f->GetArtField(), &unboxed_value)) {
+  if (!UnboxPrimitiveForField(boxed_value,
+                              MakeObjPtr(field_type),
+                              f->GetArtField(),
+                              &unboxed_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
@@ -339,7 +346,7 @@
 static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
                               const JValue& new_value) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field*>(javaField);
+  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Decode();
   mirror::Object* o = nullptr;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     return;
@@ -419,21 +426,22 @@
 static jobject Field_getAnnotationNative(JNIEnv* env, jobject javaField, jclass annotationType) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
+  ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
   if (field->GetDeclaringClass()->IsProxyClass()) {
     return nullptr;
   }
-  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
   return soa.AddLocalReference<jobject>(annotations::GetAnnotationForField(field, klass));
 }
 
 static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) {
   ScopedFastNativeObjectAccess soa(env);
-  ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
+  ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
   if (field->GetDeclaringClass()->IsProxyClass()) {
     // Return an empty array instead of a null pointer.
     mirror::Class* annotation_array_class =
-        soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_annotation_Annotation__array);
+        soa.Decode<mirror::Class>(
+            WellKnownClasses::java_lang_annotation_Annotation__array).Decode();
     mirror::ObjectArray<mirror::Object>* empty_array =
         mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
     return soa.AddLocalReference<jobjectArray>(empty_array);
@@ -443,7 +451,7 @@
 
 static jobjectArray Field_getSignatureAnnotation(JNIEnv* env, jobject javaField) {
   ScopedFastNativeObjectAccess soa(env);
-  ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
+  ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
   if (field->GetDeclaringClass()->IsProxyClass()) {
     return nullptr;
   }
@@ -454,11 +462,11 @@
                                                 jclass annotationType) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  ArtField* field = soa.Decode<mirror::Field*>(javaField)->GetArtField();
+  ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
   if (field->GetDeclaringClass()->IsProxyClass()) {
     return false;
   }
-  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
   return annotations::IsFieldAnnotationPresent(field, klass);
 }
 
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index b8efb14..b5f2f7c 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -26,7 +26,7 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "reflection.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index c2a803c..6060b8a 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -21,7 +21,7 @@
 #include "dex_file-inl.h"
 #include "dex_file_annotations.h"
 #include "jni_internal.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "utils.h"
 
 namespace art {
@@ -53,7 +53,7 @@
   }
 
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+  Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
   return soa.AddLocalReference<jobject>(
       annotations::GetAnnotationForMethodParameter(method, parameterIndex, klass));
 }
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 4a6ab40..ece0338 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -21,7 +21,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/object_array.h"
 #include "mirror/string.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "verify_object-inl.h"
 
 namespace art {
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index 64d56f6..2590452 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -18,7 +18,7 @@
 #include "mirror/string.h"
 #include "mirror/string-inl.h"
 #include "native/libcore_util_CharsetUtils.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "ScopedPrimitiveArray.h"
 #include "unicode/utf16.h"
 
@@ -154,7 +154,7 @@
                                jchar maxValidChar) {
   ScopedObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(java_string)));
+  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
   if (string.Get() == nullptr) {
     return nullptr;
   }
@@ -191,7 +191,7 @@
                                            jint length) {
   ScopedObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(java_string)));
+  Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
   if (string.Get() == nullptr) {
     return nullptr;
   }
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 0ab2979..5356498 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -19,7 +19,7 @@
 #include "base/logging.h"
 #include "debugger.h"
 #include "jni_internal.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "ScopedPrimitiveArray.h"
 
 namespace art {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 9ed0e7e..ca17c26 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -20,7 +20,7 @@
 #include "base/mutex.h"
 #include "debugger.h"
 #include "jni_internal.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "thread_list.h"
diff --git a/runtime/native/scoped_fast_native_object_access-inl.h b/runtime/native/scoped_fast_native_object_access-inl.h
new file mode 100644
index 0000000..1d73813
--- /dev/null
+++ b/runtime/native/scoped_fast_native_object_access-inl.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_INL_H_
+#define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_INL_H_
+
+#include "scoped_fast_native_object_access.h"
+
+#include "art_method-inl.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+inline ScopedFastNativeObjectAccess::ScopedFastNativeObjectAccess(JNIEnv* env)
+    : ScopedObjectAccessAlreadyRunnable(env) {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative());
+  // Don't work with raw objects in non-runnable states.
+  DCHECK_EQ(Self()->GetState(), kRunnable);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_INL_H_
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
index c4a33df..6a9365d 100644
--- a/runtime/native/scoped_fast_native_object_access.h
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -17,7 +17,8 @@
 #ifndef ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 #define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
 
-#include "art_method-inl.h"
+#include <jni.h>
+
 #include "scoped_thread_state_change.h"
 
 namespace art {
@@ -26,18 +27,11 @@
 // JNI methods.
 class ScopedFastNativeObjectAccess : public ScopedObjectAccessAlreadyRunnable {
  public:
-  explicit ScopedFastNativeObjectAccess(JNIEnv* env)
+  ALWAYS_INLINE explicit ScopedFastNativeObjectAccess(JNIEnv* env)
     REQUIRES(!Locks::thread_suspend_count_lock_)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
-     : ScopedObjectAccessAlreadyRunnable(env) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative());
-    // Don't work with raw objects in non-runnable states.
-    DCHECK_EQ(Self()->GetState(), kRunnable);
-  }
+    SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
 
-  ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
-  }
+  ALWAYS_INLINE ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScopedFastNativeObjectAccess);
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 472340c..2fae3cc 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -21,7 +21,7 @@
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_fast_native_object_access.h"
+#include "scoped_fast_native_object_access-inl.h"
 
 #include <unistd.h>
 #include <stdlib.h>
@@ -33,61 +33,64 @@
 static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                          jint expectedValue, jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   bool success = obj->CasFieldStrongSequentiallyConsistent32<false>(MemberOffset(offset),
-                                                                    expectedValue, newValue);
+                                                                    expectedValue,
+                                                                    newValue);
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                           jlong expectedValue, jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   bool success = obj->CasFieldStrongSequentiallyConsistent64<false>(MemberOffset(offset),
-                                                                    expectedValue, newValue);
+                                                                    expectedValue,
+                                                                    newValue);
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                             jobject javaExpectedValue, jobject javaNewValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
-  mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> expectedValue = soa.Decode<mirror::Object>(javaExpectedValue);
+  ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
   // JNI must use non transactional mode.
   if (kUseReadBarrier) {
     // Need to make sure the reference stored in the field is a to-space one before attempting the
     // CAS or the CAS could fail incorrectly.
     mirror::HeapReference<mirror::Object>* field_addr =
         reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
-            reinterpret_cast<uint8_t*>(obj) + static_cast<size_t>(offset));
+            reinterpret_cast<uint8_t*>(obj.Decode()) + static_cast<size_t>(offset));
     ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /*kAlwaysUpdateField*/true>(
-        obj,
+        obj.Decode(),
         MemberOffset(offset),
         field_addr);
   }
   bool success = obj->CasFieldStrongSequentiallyConsistentObject<false>(MemberOffset(offset),
-                                                                        expectedValue, newValue);
+                                                                        expectedValue.Decode(),
+                                                                        newValue.Decode());
   return success ? JNI_TRUE : JNI_FALSE;
 }
 
 static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   return obj->GetField32(MemberOffset(offset));
 }
 
 static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   return obj->GetField32Volatile(MemberOffset(offset));
 }
 
 static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   obj->SetField32<false>(MemberOffset(offset), newValue);
 }
@@ -95,7 +98,7 @@
 static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                   jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   obj->SetField32Volatile<false>(MemberOffset(offset), newValue);
 }
@@ -103,7 +106,7 @@
 static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                  jint newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   QuasiAtomic::ThreadFenceRelease();
   // JNI must use non transactional mode.
   obj->SetField32<false>(MemberOffset(offset), newValue);
@@ -111,19 +114,19 @@
 
 static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   return obj->GetField64(MemberOffset(offset));
 }
 
 static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   return obj->GetField64Volatile(MemberOffset(offset));
 }
 
 static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   obj->SetField64<false>(MemberOffset(offset), newValue);
 }
@@ -131,7 +134,7 @@
 static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                    jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   // JNI must use non transactional mode.
   obj->SetField64Volatile<false>(MemberOffset(offset), newValue);
 }
@@ -139,7 +142,7 @@
 static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                   jlong newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   QuasiAtomic::ThreadFenceRelease();
   // JNI must use non transactional mode.
   obj->SetField64<false>(MemberOffset(offset), newValue);
@@ -147,56 +150,56 @@
 
 static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* value = obj->GetFieldObjectVolatile<mirror::Object>(MemberOffset(offset));
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> value = obj->GetFieldObjectVolatile<mirror::Object>(MemberOffset(offset));
   return soa.AddLocalReference<jobject>(value);
 }
 
 static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* value = obj->GetFieldObject<mirror::Object>(MemberOffset(offset));
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> value = obj->GetFieldObject<mirror::Object>(MemberOffset(offset));
   return soa.AddLocalReference<jobject>(value);
 }
 
 static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                              jobject javaNewValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
   // JNI must use non transactional mode.
-  obj->SetFieldObject<false>(MemberOffset(offset), newValue);
+  obj->SetFieldObject<false>(MemberOffset(offset), newValue.Decode());
 }
 
 static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                      jobject javaNewValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
   // JNI must use non transactional mode.
-  obj->SetFieldObjectVolatile<false>(MemberOffset(offset), newValue);
+  obj->SetFieldObjectVolatile<false>(MemberOffset(offset), newValue.Decode());
 }
 
 static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
                                     jobject javaNewValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-  mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
   QuasiAtomic::ThreadFenceRelease();
   // JNI must use non transactional mode.
-  obj->SetFieldObject<false>(MemberOffset(offset), newValue);
+  obj->SetFieldObject<false>(MemberOffset(offset), newValue.Decode());
 }
 
 static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Class* component = soa.Decode<mirror::Class*>(component_class);
+  ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class);
   Primitive::Type primitive_type = component->GetPrimitiveType();
   return mirror::Array::DataOffset(Primitive::ComponentSize(primitive_type)).Int32Value();
 }
 
 static jint Unsafe_getArrayIndexScaleForComponentType(JNIEnv* env, jclass, jobject component_class) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Class* component = soa.Decode<mirror::Class*>(component_class);
+  ObjPtr<mirror::Class> component = soa.Decode<mirror::Class>(component_class);
   Primitive::Type primitive_type = component->GetPrimitiveType();
   return Primitive::ComponentSize(primitive_type);
 }
@@ -289,16 +292,16 @@
 
 static void Unsafe_copyMemory(JNIEnv *env, jobject unsafe ATTRIBUTE_UNUSED, jlong src,
                               jlong dst, jlong size) {
-    if (size == 0) {
-        return;
-    }
-    // size is nonnegative and fits into size_t
-    if (size < 0 || size != (jlong)(size_t) size) {
-        ScopedFastNativeObjectAccess soa(env);
-        ThrowIllegalAccessException("wrong number of bytes");
-    }
-    size_t sz = (size_t)size;
-    memcpy(reinterpret_cast<void *>(dst), reinterpret_cast<void *>(src), sz);
+  if (size == 0) {
+    return;
+  }
+  // size is nonnegative and fits into size_t
+  if (size < 0 || size != (jlong)(size_t) size) {
+    ScopedFastNativeObjectAccess soa(env);
+    ThrowIllegalAccessException("wrong number of bytes");
+  }
+  size_t sz = (size_t)size;
+  memcpy(reinterpret_cast<void *>(dst), reinterpret_cast<void *>(src), sz);
 }
 
 template<typename T>
@@ -306,12 +309,12 @@
                         size_t array_offset,
                         size_t size)
         REQUIRES_SHARED(Locks::mutator_lock_) {
-    const T* src = reinterpret_cast<T*>(srcAddr);
-    size_t sz = size / sizeof(T);
-    size_t of = array_offset / sizeof(T);
-    for (size_t i = 0; i < sz; ++i) {
-        array->Set(i + of, *(src + i));
-    }
+  const T* src = reinterpret_cast<T*>(srcAddr);
+  size_t sz = size / sizeof(T);
+  size_t of = array_offset / sizeof(T);
+  for (size_t i = 0; i < sz; ++i) {
+    array->Set(i + of, *(src + i));
+  }
 }
 
 template<typename T>
@@ -319,12 +322,12 @@
                           size_t array_offset,
                           size_t size)
         REQUIRES_SHARED(Locks::mutator_lock_) {
-    T* dst = reinterpret_cast<T*>(dstAddr);
-    size_t sz = size / sizeof(T);
-    size_t of = array_offset / sizeof(T);
-    for (size_t i = 0; i < sz; ++i) {
-        *(dst + i) = array->Get(i + of);
-    }
+  T* dst = reinterpret_cast<T*>(dstAddr);
+  size_t sz = size / sizeof(T);
+  size_t of = array_offset / sizeof(T);
+  for (size_t i = 0; i < sz; ++i) {
+    *(dst + i) = array->Get(i + of);
+  }
 }
 
 static void Unsafe_copyMemoryToPrimitiveArray(JNIEnv *env,
@@ -333,29 +336,29 @@
                                               jobject dstObj,
                                               jlong dstOffset,
                                               jlong size) {
-    ScopedObjectAccess soa(env);
-    if (size == 0) {
-        return;
-    }
-    // size is nonnegative and fits into size_t
-    if (size < 0 || size != (jlong)(size_t) size) {
-        ThrowIllegalAccessException("wrong number of bytes");
-    }
-    size_t sz = (size_t)size;
-    size_t dst_offset = (size_t)dstOffset;
-    mirror::Object* dst = soa.Decode<mirror::Object*>(dstObj);
-    mirror::Class* component_type = dst->GetClass()->GetComponentType();
-    if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-        copyToArray(srcAddr, dst->AsByteSizedArray(), dst_offset, sz);
-    } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-        copyToArray(srcAddr, dst->AsShortSizedArray(), dst_offset, sz);
-    } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-        copyToArray(srcAddr, dst->AsIntArray(), dst_offset, sz);
-    } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-        copyToArray(srcAddr, dst->AsLongArray(), dst_offset, sz);
-    } else {
-        ThrowIllegalAccessException("not a primitive array");
-    }
+  ScopedObjectAccess soa(env);
+  if (size == 0) {
+    return;
+  }
+  // size is nonnegative and fits into size_t
+  if (size < 0 || size != (jlong)(size_t) size) {
+    ThrowIllegalAccessException("wrong number of bytes");
+  }
+  size_t sz = (size_t)size;
+  size_t dst_offset = (size_t)dstOffset;
+  ObjPtr<mirror::Object> dst = soa.Decode<mirror::Object>(dstObj);
+  mirror::Class* component_type = dst->GetClass()->GetComponentType();
+  if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
+    copyToArray(srcAddr, dst->AsByteSizedArray(), dst_offset, sz);
+  } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
+    copyToArray(srcAddr, dst->AsShortSizedArray(), dst_offset, sz);
+  } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
+    copyToArray(srcAddr, dst->AsIntArray(), dst_offset, sz);
+  } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
+    copyToArray(srcAddr, dst->AsLongArray(), dst_offset, sz);
+  } else {
+    ThrowIllegalAccessException("not a primitive array");
+  }
 }
 
 static void Unsafe_copyMemoryFromPrimitiveArray(JNIEnv *env,
@@ -364,85 +367,85 @@
                                                 jlong srcOffset,
                                                 jlong dstAddr,
                                                 jlong size) {
-    ScopedObjectAccess soa(env);
-    if (size == 0) {
-        return;
-    }
-    // size is nonnegative and fits into size_t
-    if (size < 0 || size != (jlong)(size_t) size) {
-        ThrowIllegalAccessException("wrong number of bytes");
-    }
-    size_t sz = (size_t)size;
-    size_t src_offset = (size_t)srcOffset;
-    mirror::Object* src = soa.Decode<mirror::Object*>(srcObj);
-    mirror::Class* component_type = src->GetClass()->GetComponentType();
-    if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
-        copyFromArray(dstAddr, src->AsByteSizedArray(), src_offset, sz);
-    } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
-        copyFromArray(dstAddr, src->AsShortSizedArray(), src_offset, sz);
-    } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
-        copyFromArray(dstAddr, src->AsIntArray(), src_offset, sz);
-    } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
-        copyFromArray(dstAddr, src->AsLongArray(), src_offset, sz);
-    } else {
-        ThrowIllegalAccessException("not a primitive array");
-    }
+  ScopedObjectAccess soa(env);
+  if (size == 0) {
+    return;
+  }
+  // size is nonnegative and fits into size_t
+  if (size < 0 || size != (jlong)(size_t) size) {
+    ThrowIllegalAccessException("wrong number of bytes");
+  }
+  size_t sz = (size_t)size;
+  size_t src_offset = (size_t)srcOffset;
+  ObjPtr<mirror::Object> src = soa.Decode<mirror::Object>(srcObj);
+  mirror::Class* component_type = src->GetClass()->GetComponentType();
+  if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
+    copyFromArray(dstAddr, src->AsByteSizedArray(), src_offset, sz);
+  } else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
+    copyFromArray(dstAddr, src->AsShortSizedArray(), src_offset, sz);
+  } else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
+    copyFromArray(dstAddr, src->AsIntArray(), src_offset, sz);
+  } else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
+    copyFromArray(dstAddr, src->AsLongArray(), src_offset, sz);
+  } else {
+    ThrowIllegalAccessException("not a primitive array");
+  }
 }
 static jboolean Unsafe_getBoolean(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    return obj->GetFieldBoolean(MemberOffset(offset));
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  return obj->GetFieldBoolean(MemberOffset(offset));
 }
 
 static void Unsafe_putBoolean(JNIEnv* env, jobject, jobject javaObj, jlong offset, jboolean newValue) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    // JNI must use non transactional mode (SetField8 is non-transactional).
-    obj->SetFieldBoolean<false>(MemberOffset(offset), newValue);
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  // JNI must use non transactional mode (SetField8 is non-transactional).
+  obj->SetFieldBoolean<false>(MemberOffset(offset), newValue);
 }
 
 static jbyte Unsafe_getByte(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    return obj->GetFieldByte(MemberOffset(offset));
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  return obj->GetFieldByte(MemberOffset(offset));
 }
 
 static void Unsafe_putByte(JNIEnv* env, jobject, jobject javaObj, jlong offset, jbyte newValue) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    // JNI must use non transactional mode.
-    obj->SetFieldByte<false>(MemberOffset(offset), newValue);
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  // JNI must use non transactional mode.
+  obj->SetFieldByte<false>(MemberOffset(offset), newValue);
 }
 
 static jchar Unsafe_getChar(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    return obj->GetFieldChar(MemberOffset(offset));
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  return obj->GetFieldChar(MemberOffset(offset));
 }
 
 static void Unsafe_putChar(JNIEnv* env, jobject, jobject javaObj, jlong offset, jchar newValue) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    // JNI must use non transactional mode.
-    obj->SetFieldChar<false>(MemberOffset(offset), newValue);
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  // JNI must use non transactional mode.
+  obj->SetFieldChar<false>(MemberOffset(offset), newValue);
 }
 
 static jshort Unsafe_getShort(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    return obj->GetFieldShort(MemberOffset(offset));
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  return obj->GetFieldShort(MemberOffset(offset));
 }
 
 static void Unsafe_putShort(JNIEnv* env, jobject, jobject javaObj, jlong offset, jshort newValue) {
-    ScopedFastNativeObjectAccess soa(env);
-    mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
-    // JNI must use non transactional mode.
-    obj->SetFieldShort<false>(MemberOffset(offset), newValue);
+  ScopedFastNativeObjectAccess soa(env);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
+  // JNI must use non transactional mode.
+  obj->SetFieldShort<false>(MemberOffset(offset), newValue);
 }
 
 static jfloat Unsafe_getFloat(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   union {int32_t val; jfloat converted;} conv;
   conv.val = obj->GetField32(MemberOffset(offset));
   return conv.converted;
@@ -450,7 +453,7 @@
 
 static void Unsafe_putFloat(JNIEnv* env, jobject, jobject javaObj, jlong offset, jfloat newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   union {int32_t converted; jfloat val;} conv;
   conv.val = newValue;
   // JNI must use non transactional mode.
@@ -459,7 +462,7 @@
 
 static jdouble Unsafe_getDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   union {int64_t val; jdouble converted;} conv;
   conv.val = obj->GetField64(MemberOffset(offset));
   return conv.converted;
@@ -467,7 +470,7 @@
 
 static void Unsafe_putDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset, jdouble newValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
+  ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
   union {int64_t converted; jdouble val;} conv;
   conv.val = newValue;
   // JNI must use non transactional mode.
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index 155c008..059dc5a 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -26,7 +26,7 @@
 #include "base/macros.h"
 #include "dex_file-inl.h"
 #include "mirror/class-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "sigchain.h"
 
 namespace art {
@@ -43,7 +43,7 @@
   }
 
   ScopedObjectAccess soa(env);
-  mirror::Class* c = soa.Decode<mirror::Class*>(clazz);
+  ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(clazz);
 
   uint32_t native_method_count = 0;
   for (auto& m : c->GetMethods(kRuntimePointerSize)) {
@@ -58,7 +58,7 @@
     return 0;
   }
   ScopedObjectAccess soa(env);
-  mirror::Class* c = soa.Decode<mirror::Class*>(clazz);
+  ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(clazz);
 
   uint32_t count = 0;
   for (auto& m : c->GetMethods(kRuntimePointerSize)) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 12a8298..4d8687c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '8', '7', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '8', '9', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 415f991..ff00451 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -27,7 +27,7 @@
 #include "oat.h"
 #include "os.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utils.h"
 
 namespace art {
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 6ec5e55..d18e946 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -33,7 +33,7 @@
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "os.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "utils.h"
 
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 6d4b2f6..acad2a9 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -30,7 +30,7 @@
 #include "handle_scope-inl.h"
 #include "mirror/class_loader.h"
 #include "oat_file_assistant.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 
@@ -294,8 +294,8 @@
   }
 
   // Unsupported class-loader?
-  if (class_loader->GetClass() !=
-      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
+  if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
+      class_loader->GetClass()) {
     VLOG(class_linker) << "Unsupported class-loader " << PrettyClass(class_loader->GetClass());
     return false;
   }
@@ -338,10 +338,10 @@
   ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
   ArtField* const dex_file_field =
       soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  const mirror::Class* const element_class = soa.Decode<mirror::Class*>(
+  ObjPtr<mirror::Class> const element_class = soa.Decode<mirror::Class>(
       WellKnownClasses::dalvik_system_DexPathList__Element);
-  const mirror::Class* const dexfile_class = soa.Decode<mirror::Class*>(
-        WellKnownClasses::dalvik_system_DexFile);
+  ObjPtr<mirror::Class> const dexfile_class = soa.Decode<mirror::Class>(
+      WellKnownClasses::dalvik_system_DexFile);
 
   // Collect all the dex files.
   auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
@@ -361,9 +361,9 @@
     // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile.
 
     mirror::Object* dex_file;
-    if (element->GetClass() == element_class) {
+    if (element_class == element->GetClass()) {
       dex_file = dex_file_field->GetObject(element);
-    } else if (element->GetClass() == dexfile_class) {
+    } else if (dexfile_class == element->GetClass()) {
       dex_file = element;
     } else {
       LOG(WARNING) << "Unsupported element in dex_elements: " << PrettyClass(element->GetClass());
@@ -442,9 +442,9 @@
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::ClassLoader> h_class_loader =
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
     Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
-        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements));
+        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements));
     if (h_class_loader.Get() != nullptr &&
         GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) {
       class_loader_ok = true;
@@ -638,7 +638,7 @@
         ScopedObjectAccess soa(self);
         StackHandleScope<1> hs(self);
         Handle<mirror::ClassLoader> h_loader(
-            hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader)));
+            hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
         // Can not load app image without class loader.
         if (h_loader.Get() != nullptr) {
           std::string temp_error_msg;
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index a88553c..b416b9d 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -21,7 +21,7 @@
 #include <gtest/gtest.h>
 
 #include "common_runtime_test.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 0ab2bfe..a68d9f8 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -17,7 +17,7 @@
 #include "oat_quick_method_header.h"
 
 #include "art_method.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h
new file mode 100644
index 0000000..1c698b5
--- /dev/null
+++ b/runtime/obj_ptr-inl.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_OBJ_PTR_INL_H_
+#define ART_RUNTIME_OBJ_PTR_INL_H_
+
+#include "obj_ptr.h"
+#include "thread-inl.h"
+
+namespace art {
+
+template<class MirrorType, bool kPoison>
+inline bool ObjPtr<MirrorType, kPoison>::IsValid() const {
+  if (!kPoison || IsNull()) {
+    return true;
+  }
+  return GetCookie() == TrimCookie(Thread::Current()->GetPoisonObjectCookie());
+}
+
+template<class MirrorType, bool kPoison>
+inline void ObjPtr<MirrorType, kPoison>::AssertValid() const {
+  if (kPoison) {
+    CHECK(IsValid()) << "Stale object pointer " << DecodeUnchecked() << " , expected cookie "
+        << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
+  }
+}
+
+template<class MirrorType, bool kPoison>
+inline uintptr_t ObjPtr<MirrorType, kPoison>::Encode(MirrorType* ptr) {
+  uintptr_t ref = reinterpret_cast<uintptr_t>(ptr);
+  DCHECK_ALIGNED(ref, kObjectAlignment);
+  if (kPoison && ref != 0) {
+    DCHECK_LE(ref, 0xFFFFFFFFU);
+    ref >>= kObjectAlignmentShift;
+    // Put cookie in high bits.
+    Thread* self = Thread::Current();
+    DCHECK(self != nullptr);
+    ref |= self->GetPoisonObjectCookie() << kCookieShift;
+  }
+  return ref;
+}
+
+template<class MirrorType, bool kPoison>
+inline std::ostream& operator<<(std::ostream& os, ObjPtr<MirrorType, kPoison> ptr) {
+  // May be used for dumping bad pointers, do not use the checked version.
+  return os << ptr.DecodeUnchecked();
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_OBJ_PTR_INL_H_
diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h
new file mode 100644
index 0000000..d5ac33d
--- /dev/null
+++ b/runtime/obj_ptr.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ART_RUNTIME_OBJ_PTR_H_
+#define ART_RUNTIME_OBJ_PTR_H_
+
+#include <ostream>
+
+#include "base/mutex.h"  // For Locks::mutator_lock_.
+#include "globals.h"
+#include "mirror/object_reference.h"
+
+namespace art {
+
+// Value type representing a pointer to a mirror::Object of type MirrorType
+// Pass kPoison as a template boolean for testing in non-debug builds.
+// Since the cookie is thread based, it is not safe to share an ObjPtr between threads.
+template<class MirrorType, bool kPoison = kIsDebugBuild>
+class ObjPtr {
+  static constexpr size_t kCookieShift =
+      sizeof(mirror::HeapReference<mirror::Object>) * kBitsPerByte - kObjectAlignmentShift;
+  static constexpr size_t kCookieBits = sizeof(uintptr_t) * kBitsPerByte - kCookieShift;
+  static constexpr uintptr_t kCookieMask = (static_cast<uintptr_t>(1u) << kCookieBits) - 1;
+
+  static_assert(kCookieBits >= kObjectAlignmentShift,
+                "must have a least kObjectAlignmentShift bits");
+
+ public:
+  ALWAYS_INLINE ObjPtr() REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {}
+
+  ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {}
+
+  template <typename Type>
+  ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_)
+      : reference_(Encode(static_cast<MirrorType*>(ptr))) {}
+
+  template <typename Type>
+  ALWAYS_INLINE ObjPtr(const ObjPtr<Type>& other) REQUIRES_SHARED(Locks::mutator_lock_)
+      : reference_(Encode(static_cast<MirrorType*>(other.Decode()))) {}
+
+  template <typename Type>
+  ALWAYS_INLINE ObjPtr& operator=(const ObjPtr& other) {
+    reference_ = Encode(static_cast<MirrorType*>(other.Decode()));
+    return *this;
+  }
+
+  ALWAYS_INLINE ObjPtr& operator=(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+    Assign(ptr);
+    return *this;
+  }
+
+  ALWAYS_INLINE void Assign(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
+    reference_ = Encode(ptr);
+  }
+
+  ALWAYS_INLINE MirrorType* operator->() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode();
+  }
+
+  ALWAYS_INLINE bool IsNull() const {
+    return reference_ == 0;
+  }
+
+  // Decode makes sure that the object pointer is valid.
+  ALWAYS_INLINE MirrorType* Decode() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    AssertValid();
+    return DecodeUnchecked();
+  }
+
+  ALWAYS_INLINE bool IsValid() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+  ALWAYS_INLINE void AssertValid() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+  ALWAYS_INLINE bool operator==(const ObjPtr& ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() == ptr.Decode();
+  }
+
+  ALWAYS_INLINE bool operator==(const MirrorType* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() == ptr;
+  }
+
+  ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return IsNull();
+  }
+
+  ALWAYS_INLINE bool operator!=(const ObjPtr& ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() != ptr.Decode();
+  }
+
+  ALWAYS_INLINE bool operator!=(const MirrorType* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return Decode() != ptr;
+  }
+
+  ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return !IsNull();
+  }
+
+  // Decode unchecked does not check that object pointer is valid. Do not use if you can avoid it.
+  ALWAYS_INLINE MirrorType* DecodeUnchecked() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (kPoison) {
+      return reinterpret_cast<MirrorType*>(
+          static_cast<uintptr_t>(static_cast<uint32_t>(reference_ << kObjectAlignmentShift)));
+    } else {
+      return reinterpret_cast<MirrorType*>(reference_);
+    }
+  }
+
+ private:
+  // Trim off high bits of thread local cookie.
+  ALWAYS_INLINE static uintptr_t TrimCookie(uintptr_t cookie) {
+    return cookie & kCookieMask;
+  }
+
+  ALWAYS_INLINE uintptr_t GetCookie() const {
+    return reference_ >> kCookieShift;
+  }
+
+  ALWAYS_INLINE static uintptr_t Encode(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_);
+  // The encoded reference and cookie.
+  uintptr_t reference_;
+};
+
+template<class MirrorType, bool kPoison = kIsDebugBuild>
+static inline ObjPtr<MirrorType, kPoison> MakeObjPtr(MirrorType* ptr) {
+  return ObjPtr<MirrorType, kPoison>(ptr);
+}
+
+template<class MirrorType, bool kPoison>
+ALWAYS_INLINE std::ostream& operator<<(std::ostream& os, ObjPtr<MirrorType, kPoison> ptr)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_OBJ_PTR_H_
diff --git a/runtime/openjdkjvm/Android.bp b/runtime/openjdkjvm/Android.bp
index 5ed1615..37112b6 100644
--- a/runtime/openjdkjvm/Android.bp
+++ b/runtime/openjdkjvm/Android.bp
@@ -19,7 +19,10 @@
     host_supported: true,
     srcs: ["OpenjdkJvm.cc"],
     include_dirs: ["art/runtime"],
-    shared_libs: ["libnativehelper"],
+    shared_libs: [
+        "libbase",
+        "libnativehelper"
+    ],
 }
 
 art_cc_library {
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index 54ec5d3..d46d78c 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -43,7 +43,7 @@
 #include "thread_list.h"
 #include "runtime.h"
 #include "handle_scope-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "mirror/class_loader.h"
 #include "verify_object-inl.h"
@@ -52,7 +52,7 @@
 #include "../../libcore/ojluni/src/main/native/jvm.h"  // TODO(narayan): fix it
 #include "jni_internal.h"
 #include "mirror/string-inl.h"
-#include "native/scoped_fast_native_object_access.h"
+#include "native/scoped_fast_native_object_access-inl.h"
 #include "ScopedLocalRef.h"
 #include <sys/time.h>
 #include <sys/socket.h>
@@ -61,10 +61,10 @@
 #undef LOG_TAG
 #define LOG_TAG "artopenjdk"
 
-using art::WARNING;
-using art::INFO;
-using art::ERROR;
-using art::FATAL;
+using ::android::base::WARNING;
+using ::android::base::INFO;
+using ::android::base::ERROR;
+using ::android::base::FATAL;
 
 /* posix open() with extensions; used by e.g. ZipFile */
 JNIEXPORT jint JVM_Open(const char* fname, jint flags, jint mode) {
@@ -286,9 +286,8 @@
 
 JNIEXPORT jstring JVM_InternString(JNIEnv* env, jstring jstr) {
   art::ScopedFastNativeObjectAccess soa(env);
-  art::mirror::String* s = soa.Decode<art::mirror::String*>(jstr);
-  art::mirror::String* result = s->Intern();
-  return soa.AddLocalReference<jstring>(result);
+  art::ObjPtr<art::mirror::String> s = soa.Decode<art::mirror::String>(jstr);
+  return soa.AddLocalReference<jstring>(s->Intern());
 }
 
 JNIEXPORT jlong JVM_FreeMemory(void) {
@@ -364,8 +363,8 @@
 JNIEXPORT void JVM_Sleep(JNIEnv* env, jclass threadClass ATTRIBUTE_UNUSED,
                          jobject java_lock, jlong millis) {
   art::ScopedFastNativeObjectAccess soa(env);
-  art::mirror::Object* lock = soa.Decode<art::mirror::Object*>(java_lock);
-  art::Monitor::Wait(art::Thread::Current(), lock, millis, 0, true, art::kSleeping);
+  art::ObjPtr<art::mirror::Object> lock = soa.Decode<art::mirror::Object>(java_lock);
+  art::Monitor::Wait(art::Thread::Current(), lock.Decode(), millis, 0, true, art::kSleeping);
 }
 
 JNIEXPORT jobject JVM_CurrentThread(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED) {
@@ -395,19 +394,19 @@
 
 JNIEXPORT jboolean JVM_HoldsLock(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED, jobject jobj) {
   art::ScopedObjectAccess soa(env);
-  art::mirror::Object* object = soa.Decode<art::mirror::Object*>(jobj);
-  if (object == NULL) {
+  art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobj);
+  if (object == nullptr) {
     art::ThrowNullPointerException("object == null");
     return JNI_FALSE;
   }
-  return soa.Self()->HoldsLock(object);
+  return soa.Self()->HoldsLock(object.Decode());
 }
 
 JNIEXPORT void JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring java_name) {
   ScopedUtfChars name(env, java_name);
   {
     art::ScopedObjectAccess soa(env);
-    if (soa.Decode<art::mirror::Object*>(jthread) == soa.Self()->GetPeer()) {
+    if (soa.Decode<art::mirror::Object>(jthread) == soa.Self()->GetPeer()) {
       soa.Self()->SetThreadName(name.c_str());
       return;
     }
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 977ef44..d7a6c0a 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -17,9 +17,13 @@
     name: "libopenjdkjvmti_defaults",
     defaults: ["art_defaults"],
     host_supported: true,
-    srcs: ["OpenjdkJvmTi.cc"],
+    srcs: ["OpenjdkJvmTi.cc",
+           "transform.cc"],
     include_dirs: ["art/runtime"],
-    shared_libs: ["libnativehelper"],
+    shared_libs: [
+        "libbase",
+        "libnativehelper",
+    ],
 }
 
 art_cc_library {
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index d3561c1..a1a2361 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -29,15 +29,17 @@
  * questions.
  */
 
+#include <string>
+#include <vector>
+
 #include <jni.h>
+
 #include "openjdkjvmti/jvmti.h"
 
 #include "art_jvmti.h"
-#include "gc_root-inl.h"
-#include "globals.h"
 #include "jni_env_ext-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread_list.h"
+#include "runtime.h"
+#include "transform.h"
 
 // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton
 // easier to create.
@@ -904,6 +906,66 @@
   static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
     return ERR(NOT_IMPLEMENTED);
   }
+
+  // TODO Remove this once events are working.
+  static jvmtiError RetransformClassWithHook(jvmtiEnv* env,
+                                             jclass klass,
+                                             jvmtiEventClassFileLoadHook hook) {
+    std::vector<jclass> classes;
+    classes.push_back(klass);
+    return RetransformClassesWithHook(reinterpret_cast<ArtJvmTiEnv*>(env), classes, hook);
+  }
+
+  // TODO This will be called by the event handler for the art::ti Event Load Event
+  static jvmtiError RetransformClassesWithHook(ArtJvmTiEnv* env,
+                                               const std::vector<jclass>& classes,
+                                               jvmtiEventClassFileLoadHook hook) {
+    if (!IsValidEnv(env)) {
+      return ERR(INVALID_ENVIRONMENT);
+    }
+    for (jclass klass : classes) {
+      JNIEnv* jni_env = nullptr;
+      jobject loader = nullptr;
+      std::string name;
+      jobject protection_domain = nullptr;
+      jint data_len = 0;
+      unsigned char* dex_data = nullptr;
+      jvmtiError ret = OK;
+      std::string location;
+      if ((ret = GetTransformationData(env,
+                                       klass,
+                                       /*out*/&location,
+                                       /*out*/&jni_env,
+                                       /*out*/&loader,
+                                       /*out*/&name,
+                                       /*out*/&protection_domain,
+                                       /*out*/&data_len,
+                                       /*out*/&dex_data)) != OK) {
+        // TODO Do something more here? Maybe give log statements?
+        return ret;
+      }
+      jint new_data_len = 0;
+      unsigned char* new_dex_data = nullptr;
+      hook(env,
+           jni_env,
+           klass,
+           loader,
+           name.c_str(),
+           protection_domain,
+           data_len,
+           dex_data,
+           /*out*/&new_data_len,
+           /*out*/&new_dex_data);
+      // Check if anything actually changed.
+      if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) {
+        MoveTransformedFileIntoRuntime(klass, std::move(location), new_data_len, new_dex_data);
+        env->Deallocate(new_dex_data);
+      }
+      // Deallocate the old dex data.
+      env->Deallocate(dex_data);
+    }
+    return OK;
+  }
 };
 
 static bool IsJvmtiVersion(jint version) {
@@ -942,7 +1004,10 @@
 
 // The actual struct holding all of the entrypoints into the jvmti interface.
 const jvmtiInterface_1 gJvmtiInterface = {
-  nullptr,  // reserved1
+  // SPECIAL FUNCTION: RetransformClassWithHook Is normally reserved1
+  // TODO Remove once we have events working.
+  reinterpret_cast<void*>(JvmtiFunctions::RetransformClassWithHook),
+  // nullptr,  // reserved1
   JvmtiFunctions::SetEventNotificationMode,
   nullptr,  // reserved3
   JvmtiFunctions::GetAllThreads,
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
new file mode 100644
index 0000000..ac348e7
--- /dev/null
+++ b/runtime/openjdkjvmti/transform.cc
@@ -0,0 +1,362 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "transform.h"
+
+#include "class_linker.h"
+#include "dex_file.h"
+#include "gc_root-inl.h"
+#include "globals.h"
+#include "jni_env_ext-inl.h"
+#include "jvmti.h"
+#include "linear_alloc.h"
+#include "mem_map.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader-inl.h"
+#include "mirror/string-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread_list.h"
+#include "transform.h"
+#include "utf.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+
+namespace openjdkjvmti {
+
+static bool ReadChecksum(jint data_len, const unsigned char* dex, /*out*/uint32_t* res) {
+  if (data_len < static_cast<jint>(sizeof(art::DexFile::Header))) {
+    return false;
+  }
+  *res = reinterpret_cast<const art::DexFile::Header*>(dex)->checksum_;
+  return true;
+}
+
+static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
+                                                      jint data_len,
+                                                      unsigned char* dex_data) {
+  std::string error_msg;
+  std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
+      art::StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+      nullptr,
+      data_len,
+      PROT_READ|PROT_WRITE,
+      /*low_4gb*/false,
+      /*reuse*/false,
+      &error_msg));
+  if (map == nullptr) {
+    return map;
+  }
+  memcpy(map->Begin(), dex_data, data_len);
+  map->Protect(PROT_READ);
+  return map;
+}
+
+static void InvalidateExistingMethods(art::Thread* self,
+                                      art::Handle<art::mirror::Class> klass,
+                                      art::Handle<art::mirror::DexCache> cache,
+                                      const art::DexFile* dex_file)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  // Create new DexCache with new DexFile.
+  // reset dex_class_def_idx_
+  // for each method reset entry_point_from_quick_compiled_code_ to bridge
+  // for each method reset dex_code_item_offset_
+  // for each method reset dex_method_index_
+  // for each method set dex_cache_resolved_methods_ to new DexCache
+  // for each method set dex_cache_resolved_types_ to new DexCache
+  auto* runtime = art::Runtime::Current();
+  art::ClassLinker* linker = runtime->GetClassLinker();
+  art::PointerSize image_pointer_size = linker->GetImagePointerSize();
+  std::string descriptor_storage;
+  const char* descriptor = klass->GetDescriptor(&descriptor_storage);
+  // Get the new class def
+  const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
+      *dex_file, descriptor, art::ComputeModifiedUtf8Hash(descriptor));
+  CHECK(class_def != nullptr);
+  const art::DexFile::TypeId& declaring_class_id = dex_file->GetTypeId(class_def->class_idx_);
+  art::StackHandleScope<6> hs(self);
+  const art::DexFile& old_dex_file = klass->GetDexFile();
+  for (art::ArtMethod& method : klass->GetMethods(image_pointer_size)) {
+    // Find the code_item for the method then find the dex_method_index and dex_code_item_offset to
+    // set.
+    const art::DexFile::StringId* new_name_id = dex_file->FindStringId(method.GetName());
+    uint16_t method_return_idx =
+        dex_file->GetIndexForTypeId(*dex_file->FindTypeId(method.GetReturnTypeDescriptor()));
+    const auto* old_type_list = method.GetParameterTypeList();
+    std::vector<uint16_t> new_type_list;
+    for (uint32_t i = 0; old_type_list != nullptr && i < old_type_list->Size(); i++) {
+      new_type_list.push_back(
+          dex_file->GetIndexForTypeId(
+              *dex_file->FindTypeId(
+                  old_dex_file.GetTypeDescriptor(
+                      old_dex_file.GetTypeId(
+                          old_type_list->GetTypeItem(i).type_idx_)))));
+    }
+    const art::DexFile::ProtoId* proto_id = dex_file->FindProtoId(method_return_idx,
+                                                                  new_type_list);
+    CHECK(proto_id != nullptr || old_type_list == nullptr);
+    const art::DexFile::MethodId* method_id = dex_file->FindMethodId(declaring_class_id,
+                                                                      *new_name_id,
+                                                                      *proto_id);
+    CHECK(method_id != nullptr);
+    uint32_t dex_method_idx = dex_file->GetIndexForMethodId(*method_id);
+    method.SetDexMethodIndex(dex_method_idx);
+    linker->SetEntryPointsToInterpreter(&method);
+    method.SetCodeItemOffset(dex_file->FindCodeItemOffset(*class_def, dex_method_idx));
+    method.SetDexCacheResolvedMethods(cache->GetResolvedMethods(), image_pointer_size);
+    method.SetDexCacheResolvedTypes(cache->GetResolvedTypes(), image_pointer_size);
+  }
+
+  // Update the class fields.
+  // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
+  // to call GetReturnTypeDescriptor and GetParameterTypeList above).
+  klass->SetDexCache(cache.Get());
+  klass->SetDexCacheStrings(cache->GetStrings());
+  klass->SetDexClassDefIndex(dex_file->GetIndexForClassDef(*class_def));
+  klass->SetDexTypeIndex(dex_file->GetIndexForTypeId(*dex_file->FindTypeId(descriptor)));
+}
+
+// Adds the dex file.
+static art::mirror::LongArray* InsertDexFileIntoArray(art::Thread* self,
+                                                      const art::DexFile* dex,
+                                                      art::Handle<art::mirror::LongArray>& orig)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::StackHandleScope<1> hs(self);
+  CHECK_GE(orig->GetLength(), 1);
+  art::Handle<art::mirror::LongArray> ret(
+      hs.NewHandle(art::mirror::LongArray::Alloc(self, orig->GetLength() + 1)));
+  CHECK(ret.Get() != nullptr);
+  // Copy the oat-dex.
+  // TODO Should I clear the oatdex element?
+  ret->SetWithoutChecks<false>(0, orig->GetWithoutChecks(0));
+  ret->SetWithoutChecks<false>(1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex)));
+  ret->Memcpy(2, orig.Get(), 1, orig->GetLength() - 1);
+  return ret.Get();
+}
+
+// TODO Handle all types of class loaders.
+static bool FindDalvikSystemDexFileAndLoaderForClass(
+    art::Handle<art::mirror::Class> klass,
+    /*out*/art::mirror::Object** dex_file,
+    /*out*/art::mirror::ClassLoader** loader)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
+  const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
+  const char* dex_file_name = "Ldalvik/system/DexFile;";
+  const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
+  const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
+
+  art::Thread* self = art::Thread::Current();
+  CHECK(!self->IsExceptionPending());
+  art::StackHandleScope<11> hs(self);
+  art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+
+  art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
+      nullptr));
+  art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
+      self, dex_class_loader_name, null_loader)));
+
+  art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
+      "pathList", dex_path_list_name);
+  CHECK(path_list_field != nullptr);
+
+  art::ArtField* dex_path_list_element_field =
+      class_linker->FindClass(self, dex_path_list_name, null_loader)
+        ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
+  CHECK(dex_path_list_element_field != nullptr);
+
+  art::ArtField* element_dex_file_field =
+      class_linker->FindClass(self, dex_path_list_element_name, null_loader)
+        ->FindDeclaredInstanceField("dexFile", dex_file_name);
+  CHECK(element_dex_file_field != nullptr);
+
+  art::Handle<art::mirror::ClassLoader> h_class_loader(hs.NewHandle(klass->GetClassLoader()));
+  art::Handle<art::mirror::Class> loader_class(hs.NewHandle(h_class_loader->GetClass()));
+  // Check if loader is a BaseDexClassLoader
+  if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
+    LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
+               << "supported class loader type!";
+    return false;
+  }
+  art::Handle<art::mirror::Object> path_list(
+      hs.NewHandle(path_list_field->GetObject(h_class_loader.Get())));
+  CHECK(path_list.Get() != nullptr);
+  CHECK(!self->IsExceptionPending());
+  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(
+      hs.NewHandle(art::down_cast<art::mirror::ObjectArray<art::mirror::Object>*>(
+          dex_path_list_element_field->GetObject(path_list.Get()))));
+  CHECK(!self->IsExceptionPending());
+  CHECK(dex_elements_list.Get() != nullptr);
+  size_t num_elements = dex_elements_list->GetLength();
+  art::MutableHandle<art::mirror::Object> current_element(
+      hs.NewHandle<art::mirror::Object>(nullptr));
+  art::MutableHandle<art::mirror::Object> first_dex_file(
+      hs.NewHandle<art::mirror::Object>(nullptr));
+  for (size_t i = 0; i < num_elements; i++) {
+    current_element.Assign(dex_elements_list->Get(i));
+    CHECK(current_element.Get() != nullptr);
+    CHECK(!self->IsExceptionPending());
+    CHECK(dex_elements_list.Get() != nullptr);
+    CHECK_EQ(current_element->GetClass(), class_linker->FindClass(self,
+                                                                  dex_path_list_element_name,
+                                                                  null_loader));
+    // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
+    // comes from but it is more annoying because we would need to find this class. It is not
+    // necessary for proper function since we just need to be in front of the classes old dex file
+    // in the path.
+    first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get()));
+    if (first_dex_file.Get() != nullptr) {
+      *dex_file = first_dex_file.Get();
+      *loader = h_class_loader.Get();
+      return true;
+    }
+  }
+  return false;
+}
+
+// Gets the data surrounding the given class.
+jvmtiError GetTransformationData(ArtJvmTiEnv* env,
+                                 jclass klass,
+                                 /*out*/std::string* location,
+                                 /*out*/JNIEnv** jni_env_ptr,
+                                 /*out*/jobject* loader,
+                                 /*out*/std::string* name,
+                                 /*out*/jobject* protection_domain,
+                                 /*out*/jint* data_len,
+                                 /*out*/unsigned char** dex_data) {
+  jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(jni_env_ptr), JNI_VERSION_1_1);
+  if (ret != JNI_OK) {
+    // TODO Different error might be better?
+    return ERR(INTERNAL);
+  }
+  JNIEnv* jni_env = *jni_env_ptr;
+  art::ScopedObjectAccess soa(jni_env);
+  art::StackHandleScope<3> hs(art::Thread::Current());
+  art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
+  *loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
+  *name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
+  // TODO is this always null?
+  *protection_domain = nullptr;
+  const art::DexFile& dex = hs_klass->GetDexFile();
+  *location = dex.GetLocation();
+  *data_len = static_cast<jint>(dex.Size());
+  // TODO We should maybe change env->Allocate to allow us to mprotect this memory and stop writes.
+  jvmtiError alloc_error = env->Allocate(*data_len, dex_data);
+  if (alloc_error != OK) {
+    return alloc_error;
+  }
+  // Copy the data into a temporary buffer.
+  memcpy(reinterpret_cast<void*>(*dex_data),
+          reinterpret_cast<const void*>(dex.Begin()),
+          *data_len);
+  return OK;
+}
+
+// Install the new dex file.
+// TODO do error checks for bad state (method in a stack, changes to number of methods/fields/etc).
+jvmtiError MoveTransformedFileIntoRuntime(jclass jklass,
+                                          std::string original_location,
+                                          jint data_len,
+                                          unsigned char* dex_data) {
+  const char* dex_file_name = "Ldalvik/system/DexFile;";
+  art::Thread* self = art::Thread::Current();
+  art::Runtime* runtime = art::Runtime::Current();
+  art::ThreadList* threads = runtime->GetThreadList();
+  art::ClassLinker* class_linker = runtime->GetClassLinker();
+  uint32_t checksum = 0;
+  if (!ReadChecksum(data_len, dex_data, &checksum)) {
+    return ERR(INVALID_CLASS_FORMAT);
+  }
+
+  std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_location, data_len, dex_data));
+  if (map.get() == nullptr) {
+    return ERR(INTERNAL);
+  }
+  std::string error_msg;
+  // Load the new dex_data in memory (mmap it, etc)
+  std::unique_ptr<const art::DexFile> new_dex_file = art::DexFile::Open(map->GetName(),
+                                                                        checksum,
+                                                                        std::move(map),
+                                                                        /*verify*/ true,
+                                                                        /*verify_checksum*/ true,
+                                                                        &error_msg);
+  CHECK(new_dex_file.get() != nullptr) << "Unable to load dex file! " << error_msg;
+
+  // Get mutator lock. We need the lifetimes of these variables (hs, the classes, etc.) to be longer
+  // then current lock (since there isn't upgrading of the lock) so we don't use soa.
+  art::ThreadState old_state = self->TransitionFromSuspendedToRunnable();
+  // This scope is needed to make sure that the HandleScope dies with mutator_lock_ since we need to
+  // upgrade the mutator_lock during the execution.
+  {
+    art::StackHandleScope<11> hs(self);
+    art::Handle<art::mirror::ClassLoader> null_loader(
+        hs.NewHandle<art::mirror::ClassLoader>(nullptr));
+    CHECK(null_loader.Get() == nullptr);
+    art::ArtField* dex_file_cookie_field = class_linker->
+        FindClass(self, dex_file_name, null_loader)->
+        FindDeclaredInstanceField("mCookie", "Ljava/lang/Object;");
+    art::ArtField* dex_file_internal_cookie_field =
+        class_linker->FindClass(self, dex_file_name, null_loader)
+          ->FindDeclaredInstanceField("mInternalCookie", "Ljava/lang/Object;");
+    CHECK(dex_file_cookie_field != nullptr);
+    art::Handle<art::mirror::Class> klass(
+        hs.NewHandle(art::down_cast<art::mirror::Class*>(self->DecodeJObject(jklass))));
+    art::mirror::Object* dex_file_ptr = nullptr;
+    art::mirror::ClassLoader* class_loader_ptr = nullptr;
+    // Find dalvik.system.DexFile that represents the dex file we are changing.
+    if (!FindDalvikSystemDexFileAndLoaderForClass(klass, &dex_file_ptr, &class_loader_ptr)) {
+      self->TransitionFromRunnableToSuspended(old_state);
+      LOG(ERROR) << "Could not find DexFile.";
+      return ERR(INTERNAL);
+    }
+    art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(dex_file_ptr));
+    art::Handle<art::mirror::ClassLoader> class_loader(hs.NewHandle(class_loader_ptr));
+    art::Handle<art::mirror::LongArray> art_dex_array(
+        hs.NewHandle<art::mirror::LongArray>(
+            dex_file_cookie_field->GetObject(dex_file_obj.Get())->AsLongArray()));
+    art::Handle<art::mirror::LongArray> new_art_dex_array(
+        hs.NewHandle<art::mirror::LongArray>(
+            InsertDexFileIntoArray(self, new_dex_file.get(), art_dex_array)));
+    art::Handle<art::mirror::DexCache> cache(
+        hs.NewHandle(class_linker->RegisterDexFile(*new_dex_file.get(), class_loader.Get())));
+    self->TransitionFromRunnableToSuspended(old_state);
+
+    threads->SuspendAll("moving dex file into runtime", /*long_suspend*/true);
+    // Change the mCookie field. Old value will be GC'd as normal.
+    dex_file_cookie_field->SetObject<false>(dex_file_obj.Get(), new_art_dex_array.Get());
+    dex_file_internal_cookie_field->SetObject<false>(dex_file_obj.Get(), new_art_dex_array.Get());
+    // Invalidate existing methods.
+    InvalidateExistingMethods(self, klass, cache, new_dex_file.release());
+  }
+  threads->ResumeAll();
+  return OK;
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h
new file mode 100644
index 0000000..85bcb00
--- /dev/null
+++ b/runtime/openjdkjvmti/transform.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
+
+#include <string>
+
+#include <jni.h>
+
+#include "art_jvmti.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+// Gets the data surrounding the given class.
+jvmtiError GetTransformationData(ArtJvmTiEnv* env,
+                                 jclass klass,
+                                 /*out*/std::string* location,
+                                 /*out*/JNIEnv** jni_env_ptr,
+                                 /*out*/jobject* loader,
+                                 /*out*/std::string* name,
+                                 /*out*/jobject* protection_domain,
+                                 /*out*/jint* data_len,
+                                 /*out*/unsigned char** dex_data);
+
+// Install the new dex file.
+jvmtiError MoveTransformedFileIntoRuntime(jclass jklass,
+                                          std::string original_location,
+                                          jint data_len,
+                                          unsigned char* dex_data);
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_
+
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4f70b04..f937ca7 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -761,6 +761,8 @@
                        "(Enable new and experimental agent support)\n");
   UsageMessage(stream, "  -Xexperimental:agents"
                        "(Enable new and experimental agent support)\n");
+  UsageMessage(stream, "  -Xexperimental:method-handles"
+                       "(Enable new and experimental method handles support)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index e3f92c7..43b0b3d 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -23,7 +23,7 @@
 #include "common_compiler_test.h"
 #include "mirror/field-inl.h"
 #include "mirror/method.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -108,7 +108,7 @@
   jobject jclass_loader = LoadDex("Interfaces");
   StackHandleScope<4> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
 
   Handle<mirror::Class> I(hs.NewHandle(
       class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
@@ -142,7 +142,7 @@
   jobject jclass_loader = LoadDex("Interfaces");
   StackHandleScope<9> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
 
   Handle<mirror::Class> I(hs.NewHandle(
       class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
@@ -200,7 +200,7 @@
   jobject jclass_loader = LoadDex("Interfaces");
   StackHandleScope<7> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
 
   Handle<mirror::Class> proxyClass0;
   Handle<mirror::Class> proxyClass1;
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index b3f29c2..9056d96 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -145,7 +145,7 @@
   if (kDebugExceptionDelivery) {
     mirror::String* msg = exception->GetDetailMessage();
     std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : "");
-    self_->DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
+    self_->DumpStack(LOG_STREAM(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
                      << ": " << str_msg << "\n");
   }
   StackHandleScope<1> hs(self_);
@@ -218,7 +218,7 @@
   DCHECK(handler_method_ != nullptr && handler_method_header_->IsOptimized());
 
   if (kDebugExceptionDelivery) {
-    self_->DumpStack(LOG(INFO) << "Setting catch phis: ");
+    self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: ");
   }
 
   const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_;
@@ -520,7 +520,7 @@
 void QuickExceptionHandler::DeoptimizeStack() {
   DCHECK(is_deoptimization_);
   if (kDebugExceptionDelivery) {
-    self_->DumpStack(LOG(INFO) << "Deoptimizing: ");
+    self_->DumpStack(LOG_STREAM(INFO) << "Deoptimizing: ");
   }
 
   DeoptimizeStackVisitor visitor(self_, context_, this, false);
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index f04d41d..0be79ef 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -192,6 +192,13 @@
       } else {
         StringAppendF(&extras, " \"%.16s... (%d chars)", utf8.c_str(), s->GetLength());
       }
+    } else if (ref->IsReferenceInstance()) {
+      mirror::Object* referent = ref->AsReference()->GetReferent();
+      if (referent == nullptr) {
+        extras = " (referent is null)";
+      } else {
+        extras = StringPrintf(" (referent is a %s)", PrettyTypeOf(referent).c_str());
+      }
     }
     os << StringPrintf("    %5d: ", idx) << ref << " " << className << extras << "\n";
   }
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index fae8e72..489db9a 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,18 +16,55 @@
 
 #include "reference_table.h"
 
+#include "class_linker.h"
 #include "common_runtime_test.h"
+#include "handle_scope-inl.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
 #include "mirror/string.h"
 #include "primitive.h"
-#include "scoped_thread_state_change.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
 
 class ReferenceTableTest : public CommonRuntimeTest {};
 
+static mirror::Object* CreateWeakReference(mirror::Object* referent)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Thread* self = Thread::Current();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+  StackHandleScope<3> scope(self);
+  Handle<mirror::Object> h_referent(scope.NewHandle<mirror::Object>(referent));
+
+  Handle<mirror::Class> h_ref_class(scope.NewHandle<mirror::Class>(
+      class_linker->FindClass(self,
+                              "Ljava/lang/ref/WeakReference;",
+                              ScopedNullHandle<mirror::ClassLoader>())));
+  CHECK(h_ref_class.Get() != nullptr);
+  CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true));
+
+  Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>(
+      h_ref_class->AllocObject(self)));
+  CHECK(h_ref_instance.Get() != nullptr);
+
+  ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
+      "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
+  CHECK(constructor != nullptr);
+
+  uint32_t args[2];
+  args[0] = PointerToLowMemUInt32(h_ref_instance.Get());
+  args[1] = PointerToLowMemUInt32(h_referent.Get());
+  JValue result;
+  constructor->Invoke(self, args, sizeof(uint32_t), &result, constructor->GetShorty());
+  CHECK(!self->IsExceptionPending());
+
+  return h_ref_instance.Get();
+}
+
 TEST_F(ReferenceTableTest, Basics) {
   ScopedObjectAccess soa(Thread::Current());
   mirror::Object* o1 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello");
@@ -104,6 +141,29 @@
                 std::string::npos) << oss.str();
     }
   }
+
+  // Add a reference and check that the type of the referent is dumped.
+  {
+    mirror::Object* empty_reference = CreateWeakReference(nullptr);
+    ASSERT_TRUE(empty_reference->IsReferenceInstance());
+    rt.Add(empty_reference);
+    std::ostringstream oss;
+    rt.Dump(oss);
+    EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is null)"), std::string::npos)
+        << oss.str();
+  }
+
+  {
+    mirror::Object* string_referent = mirror::String::AllocFromModifiedUtf8(Thread::Current(), "A");
+    mirror::Object* non_empty_reference = CreateWeakReference(string_referent);
+    ASSERT_TRUE(non_empty_reference->IsReferenceInstance());
+    rt.Add(non_empty_reference);
+    std::ostringstream oss;
+    rt.Dump(oss);
+    EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is a java.lang.String)"),
+              std::string::npos)
+        << oss.str();
+  }
 }
 
 }  // namespace art
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index f54d4ca..d7db8a4 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -23,14 +23,17 @@
 #include "common_throws.h"
 #include "jvalue.h"
 #include "mirror/object-inl.h"
+#include "obj_ptr-inl.h"
 #include "primitive.h"
 #include "utils.h"
 
 namespace art {
 
 inline bool ConvertPrimitiveValue(bool unbox_for_result,
-                                  Primitive::Type srcType, Primitive::Type dstType,
-                                  const JValue& src, JValue* dst) {
+                                  Primitive::Type srcType,
+                                  Primitive::Type dstType,
+                                  const JValue& src,
+                                  JValue* dst) {
   DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
   if (LIKELY(srcType == dstType)) {
     dst->SetJ(src.GetJ());
@@ -100,11 +103,11 @@
   return false;
 }
 
-inline bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
+inline bool VerifyObjectIsClass(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c) {
   if (UNLIKELY(o == nullptr)) {
     ThrowNullPointerException("null receiver");
     return false;
-  } else if (UNLIKELY(!o->InstanceOf(c))) {
+  } else if (UNLIKELY(!o->InstanceOf(c.Decode()))) {
     InvalidReceiverError(o, c);
     return false;
   }
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 67e3fe8..b663b4c 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -24,11 +24,11 @@
 #include "dex_file-inl.h"
 #include "indirect_reference_table-inl.h"
 #include "jni_internal.h"
-#include "mirror/abstract_method.h"
 #include "mirror/class-inl.h"
+#include "mirror/executable.h"
 #include "mirror/object_array-inl.h"
 #include "nth_caller_visitor.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack_reference.h"
 #include "well_known_classes.h"
 
@@ -72,8 +72,8 @@
     num_bytes_ += 4;
   }
 
-  void Append(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
-    Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue());
+  void Append(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+    Append(StackReference<mirror::Object>::FromMirrorPtr(obj.Decode()).AsVRegValue());
   }
 
   void AppendWide(uint64_t value) {
@@ -95,7 +95,8 @@
   }
 
   void BuildArgArrayFromVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
-                                mirror::Object* receiver, va_list ap)
+                                ObjPtr<mirror::Object> receiver,
+                                va_list ap)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Set receiver if non-null (method is not static)
     if (receiver != nullptr) {
@@ -114,7 +115,7 @@
           AppendFloat(va_arg(ap, jdouble));
           break;
         case 'L':
-          Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject)));
+          Append(soa.Decode<mirror::Object>(va_arg(ap, jobject)));
           break;
         case 'D':
           AppendDouble(va_arg(ap, jdouble));
@@ -131,7 +132,7 @@
   }
 
   void BuildArgArrayFromJValues(const ScopedObjectAccessAlreadyRunnable& soa,
-                                mirror::Object* receiver, jvalue* args)
+                                ObjPtr<mirror::Object> receiver, jvalue* args)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Set receiver if non-null (method is not static)
     if (receiver != nullptr) {
@@ -156,7 +157,7 @@
           Append(args[args_offset].i);
           break;
         case 'L':
-          Append(soa.Decode<mirror::Object*>(args[args_offset].l));
+          Append(soa.Decode<mirror::Object>(args[args_offset].l));
           break;
         case 'D':
         case 'J':
@@ -212,8 +213,9 @@
                      PrettyDescriptor(found_descriptor).c_str()).c_str());
   }
 
-  bool BuildArgArrayFromObjectArray(mirror::Object* receiver,
-                                    mirror::ObjectArray<mirror::Object>* args, ArtMethod* m)
+  bool BuildArgArrayFromObjectArray(ObjPtr<mirror::Object> receiver,
+                                    ObjPtr<mirror::ObjectArray<mirror::Object>> args,
+                                    ArtMethod* m)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const DexFile::TypeList* classes = m->GetParameterTypeList();
     // Set receiver if non-null (method is not static)
@@ -221,13 +223,13 @@
       Append(receiver);
     }
     for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
-      mirror::Object* arg = args->Get(args_offset);
+      ObjPtr<mirror::Object> arg(args->Get(args_offset));
       if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
         PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-        mirror::Class* dst_class =
+        ObjPtr<mirror::Class> dst_class(
             m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
                                      true /* resolve */,
-                                     pointer_size);
+                                     pointer_size));
         if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
           ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
@@ -240,15 +242,15 @@
       }
 
 #define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
-          if (LIKELY(arg != nullptr && arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
+          if (LIKELY(arg != nullptr && arg->GetClass()->DescriptorEquals(match_descriptor))) { \
             ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
-            append(primitive_field-> get_fn(arg));
+            append(primitive_field-> get_fn(arg.Decode()));
 
 #define DO_ARG(match_descriptor, get_fn, append) \
           } else if (LIKELY(arg != nullptr && \
                             arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
             ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
-            append(primitive_field-> get_fn(arg));
+            append(primitive_field-> get_fn(arg.Decode()));
 
 #define DO_FAIL(expected) \
           } else { \
@@ -362,9 +364,9 @@
   PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (uint32_t i = 0; i < num_params; i++) {
     uint16_t type_idx = params->GetTypeItem(i).type_idx_;
-    mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx,
-                                                         true /* resolve*/,
-                                                         pointer_size);
+    ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
+                                                              true /* resolve*/,
+                                                              pointer_size));
     if (param_type == nullptr) {
       CHECK(self->IsExceptionPending());
       LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
@@ -376,7 +378,7 @@
       // TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension,
       // this is a hard to fix problem since the args can contain Object*, we need to save and
       // restore them by using a visitor similar to the ones used in the trampoline entrypoints.
-      mirror::Object* argument =
+      ObjPtr<mirror::Object> argument =
           (reinterpret_cast<StackReference<mirror::Object>*>(&args[i + offset]))->AsMirrorPtr();
       if (argument != nullptr && !argument->InstanceOf(param_type)) {
         LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
@@ -423,7 +425,7 @@
   }
 }
 
-static ArtMethod* FindVirtualMethod(mirror::Object* receiver, ArtMethod* method)
+static ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, kRuntimePointerSize);
 }
@@ -455,9 +457,9 @@
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
-    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    method = WellKnownClasses::StringInitToStringFactory(method);
   }
-  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+  ObjPtr<mirror::Object> receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object>(obj);
   uint32_t shorty_len = 0;
   const char* shorty =
       method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
@@ -486,9 +488,9 @@
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
-    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    method = WellKnownClasses::StringInitToStringFactory(method);
   }
-  mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+  ObjPtr<mirror::Object> receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object>(obj);
   uint32_t shorty_len = 0;
   const char* shorty =
       method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
@@ -513,12 +515,12 @@
     return JValue();
   }
 
-  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+  ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
   ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
-    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    method = WellKnownClasses::StringInitToStringFactory(method);
     receiver = nullptr;
   }
   uint32_t shorty_len = 0;
@@ -545,12 +547,12 @@
     return JValue();
   }
 
-  mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+  ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
   ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
   bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
   if (is_string_init) {
     // Replace calls to String.<init> with equivalent StringFactory call.
-    method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+    method = WellKnownClasses::StringInitToStringFactory(method);
     receiver = nullptr;
   }
   uint32_t shorty_len = 0;
@@ -578,30 +580,28 @@
     return nullptr;
   }
 
-  auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(javaMethod);
-  const bool accessible = abstract_method->IsAccessible();
-  ArtMethod* m = abstract_method->GetArtMethod();
+  ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(javaMethod);
+  const bool accessible = executable->IsAccessible();
+  ArtMethod* m = executable->GetArtMethod();
 
-  mirror::Class* declaring_class = m->GetDeclaringClass();
+  ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass();
   if (UNLIKELY(!declaring_class->IsInitialized())) {
     StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
+    HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&declaring_class));
     if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), h_class, true, true)) {
       return nullptr;
     }
-    declaring_class = h_class.Get();
   }
 
-  mirror::Object* receiver = nullptr;
+  ObjPtr<mirror::Object> receiver;
   if (!m->IsStatic()) {
     // Replace calls to String.<init> with equivalent StringFactory call.
     if (declaring_class->IsStringClass() && m->IsConstructor()) {
-      jmethodID mid = soa.EncodeMethod(m);
-      m = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+      m = WellKnownClasses::StringInitToStringFactory(m);
       CHECK(javaReceiver == nullptr);
     } else {
       // Check that the receiver is non-null and an instance of the field's declaring class.
-      receiver = soa.Decode<mirror::Object*>(javaReceiver);
+      receiver = soa.Decode<mirror::Object>(javaReceiver);
       if (!VerifyObjectIsClass(receiver, declaring_class)) {
         return nullptr;
       }
@@ -612,7 +612,8 @@
   }
 
   // Get our arrays of arguments and their types, and check they're the same size.
-  auto* objects = soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs);
+  ObjPtr<mirror::ObjectArray<mirror::Object>> objects =
+      soa.Decode<mirror::ObjectArray<mirror::Object>>(javaArgs);
   auto* np_method = m->GetInterfaceMethodIfProxy(kRuntimePointerSize);
   const DexFile::TypeList* classes = np_method->GetParameterTypeList();
   uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size();
@@ -624,7 +625,7 @@
   }
 
   // If method is not set to be accessible, verify it can be accessed by the caller.
-  mirror::Class* calling_class = nullptr;
+  ObjPtr<mirror::Class> calling_class;
   if (!accessible && !VerifyAccess(soa.Self(),
                                    receiver,
                                    declaring_class,
@@ -675,12 +676,13 @@
   }
 
   // Box if necessary and return.
-  return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result));
+  return soa.AddLocalReference<jobject>(
+      BoxPrimitive(Primitive::GetType(shorty[0]), result).Decode());
 }
 
-mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) {
+ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value) {
   if (src_class == Primitive::kPrimNot) {
-    return value.GetL();
+    return MakeObjPtr(value.GetL());
   }
   if (src_class == Primitive::kPrimVoid) {
     // There's no such thing as a void field, and void methods invoked via reflection return null.
@@ -751,8 +753,9 @@
   return "result";
 }
 
-static bool UnboxPrimitive(mirror::Object* o,
-                           mirror::Class* dst_class, ArtField* f,
+static bool UnboxPrimitive(ObjPtr<mirror::Object> o,
+                           ObjPtr<mirror::Class> dst_class,
+                           ArtField* f,
                            JValue* unboxed_value)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   bool unbox_for_result = (f == nullptr);
@@ -770,7 +773,7 @@
       }
       return false;
     }
-    unboxed_value->SetL(o);
+    unboxed_value->SetL(o.Decode());
     return true;
   }
   if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
@@ -792,34 +795,34 @@
   }
 
   JValue boxed_value;
-  mirror::Class* klass = o->GetClass();
-  mirror::Class* src_class = nullptr;
+  ObjPtr<mirror::Class> klass = o->GetClass();
+  ObjPtr<mirror::Class> src_class = nullptr;
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
   if (klass->DescriptorEquals("Ljava/lang/Boolean;")) {
     src_class = class_linker->FindPrimitiveClass('Z');
-    boxed_value.SetZ(primitive_field->GetBoolean(o));
+    boxed_value.SetZ(primitive_field->GetBoolean(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Byte;")) {
     src_class = class_linker->FindPrimitiveClass('B');
-    boxed_value.SetB(primitive_field->GetByte(o));
+    boxed_value.SetB(primitive_field->GetByte(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Character;")) {
     src_class = class_linker->FindPrimitiveClass('C');
-    boxed_value.SetC(primitive_field->GetChar(o));
+    boxed_value.SetC(primitive_field->GetChar(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Float;")) {
     src_class = class_linker->FindPrimitiveClass('F');
-    boxed_value.SetF(primitive_field->GetFloat(o));
+    boxed_value.SetF(primitive_field->GetFloat(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Double;")) {
     src_class = class_linker->FindPrimitiveClass('D');
-    boxed_value.SetD(primitive_field->GetDouble(o));
+    boxed_value.SetD(primitive_field->GetDouble(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Integer;")) {
     src_class = class_linker->FindPrimitiveClass('I');
-    boxed_value.SetI(primitive_field->GetInt(o));
+    boxed_value.SetI(primitive_field->GetInt(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Long;")) {
     src_class = class_linker->FindPrimitiveClass('J');
-    boxed_value.SetJ(primitive_field->GetLong(o));
+    boxed_value.SetJ(primitive_field->GetLong(o.Decode()));
   } else if (klass->DescriptorEquals("Ljava/lang/Short;")) {
     src_class = class_linker->FindPrimitiveClass('S');
-    boxed_value.SetS(primitive_field->GetShort(o));
+    boxed_value.SetS(primitive_field->GetShort(o.Decode()));
   } else {
     std::string temp;
     ThrowIllegalArgumentException(
@@ -834,28 +837,36 @@
                                boxed_value, unboxed_value);
 }
 
-bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, ArtField* f,
+bool UnboxPrimitiveForField(ObjPtr<mirror::Object> o,
+                            ObjPtr<mirror::Class> dst_class,
+                            ArtField* f,
                             JValue* unboxed_value) {
   DCHECK(f != nullptr);
   return UnboxPrimitive(o, dst_class, f, unboxed_value);
 }
 
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value) {
+bool UnboxPrimitiveForResult(ObjPtr<mirror::Object> o,
+                             ObjPtr<mirror::Class> dst_class,
+                             JValue* unboxed_value) {
   return UnboxPrimitive(o, dst_class, nullptr, unboxed_value);
 }
 
-mirror::Class* GetCallingClass(Thread* self, size_t num_frames) {
+ObjPtr<mirror::Class> GetCallingClass(Thread* self, size_t num_frames) {
   NthCallerVisitor visitor(self, num_frames);
   visitor.WalkStack();
   return visitor.caller != nullptr ? visitor.caller->GetDeclaringClass() : nullptr;
 }
 
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
-                  uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) {
+bool VerifyAccess(Thread* self,
+                  ObjPtr<mirror::Object> obj,
+                  ObjPtr<mirror::Class> declaring_class,
+                  uint32_t access_flags,
+                  ObjPtr<mirror::Class>* calling_class,
+                  size_t num_frames) {
   if ((access_flags & kAccPublic) != 0) {
     return true;
   }
-  auto* klass = GetCallingClass(self, num_frames);
+  ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
   if (UNLIKELY(klass == nullptr)) {
     // The caller is an attached native thread.
     return false;
@@ -864,10 +875,10 @@
   return VerifyAccess(obj, declaring_class, access_flags, klass);
 }
 
-bool VerifyAccess(mirror::Object* obj,
-                  mirror::Class* declaring_class,
+bool VerifyAccess(ObjPtr<mirror::Object> obj,
+                  ObjPtr<mirror::Class> declaring_class,
                   uint32_t access_flags,
-                  mirror::Class* calling_class) {
+                  ObjPtr<mirror::Class> calling_class) {
   if (calling_class == declaring_class) {
     return true;
   }
@@ -877,16 +888,16 @@
   }
   if ((access_flags & kAccProtected) != 0) {
     if (obj != nullptr && !obj->InstanceOf(calling_class) &&
-        !declaring_class->IsInSamePackage(calling_class)) {
+        !declaring_class->IsInSamePackage(calling_class.Decode())) {
       return false;
-    } else if (declaring_class->IsAssignableFrom(calling_class)) {
+    } else if (declaring_class->IsAssignableFrom(calling_class.Decode())) {
       return true;
     }
   }
-  return declaring_class->IsInSamePackage(calling_class);
+  return declaring_class->IsInSamePackage(calling_class.Decode());
 }
 
-void InvalidReceiverError(mirror::Object* o, mirror::Class* c) {
+void InvalidReceiverError(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c) {
   std::string expected_class_name(PrettyDescriptor(c));
   std::string actual_class_name(PrettyTypeOf(o));
   ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s",
@@ -896,18 +907,18 @@
 
 // This only works if there's one reference which points to the object in obj.
 // Will need to be fixed if there's cases where it's not.
-void UpdateReference(Thread* self, jobject obj, mirror::Object* result) {
+void UpdateReference(Thread* self, jobject obj, ObjPtr<mirror::Object> result) {
   IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
   IndirectRefKind kind = GetIndirectRefKind(ref);
   if (kind == kLocal) {
-    self->GetJniEnv()->locals.Update(obj, result);
+    self->GetJniEnv()->locals.Update(obj, result.Decode());
   } else if (kind == kHandleScopeOrInvalid) {
     LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid";
   } else if (kind == kGlobal) {
-    self->GetJniEnv()->vm->UpdateGlobal(self, ref, result);
+    self->GetJniEnv()->vm->UpdateGlobal(self, ref, result.Decode());
   } else {
     DCHECK_EQ(kind, kWeakGlobal);
-    self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result);
+    self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result.Decode());
   }
 }
 
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 208b533..6e5ef71 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -19,6 +19,7 @@
 
 #include "base/mutex.h"
 #include "jni.h"
+#include "obj_ptr.h"
 #include "primitive.h"
 
 namespace art {
@@ -32,62 +33,85 @@
 class ScopedObjectAccessAlreadyRunnable;
 class ShadowFrame;
 
-mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
+ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value)
     REQUIRES_SHARED(Locks::mutator_lock_);
-bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, ArtField* f,
+
+bool UnboxPrimitiveForField(ObjPtr<mirror::Object> o,
+                            ObjPtr<mirror::Class> dst_class,
+                            ArtField* f,
                             JValue* unboxed_value)
     REQUIRES_SHARED(Locks::mutator_lock_);
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value)
+
+bool UnboxPrimitiveForResult(ObjPtr<mirror::Object> o,
+                             ObjPtr<mirror::Class> dst_class,
+                             JValue* unboxed_value)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result,
-                                         Primitive::Type src_class, Primitive::Type dst_class,
-                                         const JValue& src, JValue* dst)
+                                         Primitive::Type src_class,
+                                         Primitive::Type dst_class,
+                                         const JValue& src,
+                                         JValue* dst)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         jmethodID mid,
                          va_list args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
+                         jobject obj,
+                         jmethodID mid,
                          jvalue* args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           jobject obj, jmethodID mid, jvalue* args)
+                                           jobject obj,
+                                           jmethodID mid,
+                                           jvalue* args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
-                                           jobject obj, jmethodID mid, va_list args)
+                                           jobject obj,
+                                           jmethodID mid,
+                                           va_list args)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // num_frames is number of frames we look up for access check.
-jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver,
-                     jobject args, size_t num_frames = 1)
+jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa,
+                     jobject method,
+                     jobject receiver,
+                     jobject args,
+                     size_t num_frames = 1)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-ALWAYS_INLINE bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
+ALWAYS_INLINE bool VerifyObjectIsClass(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
-                  uint32_t access_flags, mirror::Class** calling_class, size_t num_frames)
+bool VerifyAccess(Thread* self,
+                  ObjPtr<mirror::Object> obj,
+                  ObjPtr<mirror::Class> declaring_class,
+                  uint32_t access_flags,
+                  ObjPtr<mirror::Class>* calling_class,
+                  size_t num_frames)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // This version takes a known calling class.
-bool VerifyAccess(mirror::Object* obj,
-                  mirror::Class* declaring_class,
+bool VerifyAccess(ObjPtr<mirror::Object> obj,
+                  ObjPtr<mirror::Class> declaring_class,
                   uint32_t access_flags,
-                  mirror::Class* calling_class)
+                  ObjPtr<mirror::Class> calling_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Get the calling class by using a stack visitor, may return null for unattached native threads.
-mirror::Class* GetCallingClass(Thread* self, size_t num_frames)
+ObjPtr<mirror::Class> GetCallingClass(Thread* self, size_t num_frames)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-void InvalidReceiverError(mirror::Object* o, mirror::Class* c)
+void InvalidReceiverError(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-void UpdateReference(Thread* self, jobject obj, mirror::Object* result)
+void UpdateReference(Thread* self, jobject obj, ObjPtr<mirror::Object> result)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 }  // namespace art
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 4876ff0..189ed03 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -23,7 +23,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "common_compiler_test.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -93,16 +93,12 @@
     StackHandleScope<2> hs(self);
     Handle<mirror::ClassLoader> class_loader(
         hs.NewHandle(
-            ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader)));
-    if (is_static) {
-      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
-                     class_name);
-    } else {
+            ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader>(jclass_loader)));
+    if (!is_static) {
       MakeExecutable(nullptr, "java.lang.Class");
       MakeExecutable(nullptr, "java.lang.Object");
-      MakeExecutable(ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader),
-                     class_name);
     }
+    MakeExecutable(class_loader.Get(), class_name);
 
     mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
                                                 class_loader);
@@ -512,7 +508,7 @@
   jobject jclass_loader = LoadDex("Main");
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
   CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
 
   mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f144b98..df0dca0 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -88,6 +88,8 @@
 #include "mirror/class_loader.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
+#include "mirror/method_handle_impl.h"
+#include "mirror/method_type.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/throwable.h"
 #include "monitor.h"
@@ -108,9 +110,9 @@
 #include "native/java_lang_VMClassLoader.h"
 #include "native/java_lang_ref_FinalizerReference.h"
 #include "native/java_lang_ref_Reference.h"
-#include "native/java_lang_reflect_AbstractMethod.h"
 #include "native/java_lang_reflect_Array.h"
 #include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Executable.h"
 #include "native/java_lang_reflect_Field.h"
 #include "native/java_lang_reflect_Method.h"
 #include "native/java_lang_reflect_Parameter.h"
@@ -131,7 +133,7 @@
 #include "reflection.h"
 #include "runtime_options.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "sigchain.h"
 #include "signal_catcher.h"
 #include "signal_set.h"
@@ -255,7 +257,7 @@
     // This can't be called from the Heap destructor below because it
     // could call RosAlloc::InspectAll() which needs the thread_list
     // to be still alive.
-    heap_->DumpGcPerformanceInfo(LOG(INFO));
+    heap_->DumpGcPerformanceInfo(LOG_STREAM(INFO));
   }
 
   Thread* self = Thread::Current();
@@ -433,14 +435,14 @@
   // Many people have difficulty distinguish aborts from crashes,
   // so be explicit.
   AbortState state;
-  LOG(INTERNAL_FATAL) << Dumpable<AbortState>(state);
+  LOG(FATAL_WITHOUT_ABORT) << Dumpable<AbortState>(state);
 
   // Call the abort hook if we have one.
   if (Runtime::Current() != nullptr && Runtime::Current()->abort_ != nullptr) {
-    LOG(INTERNAL_FATAL) << "Calling abort hook...";
+    LOG(FATAL_WITHOUT_ABORT) << "Calling abort hook...";
     Runtime::Current()->abort_();
     // notreached
-    LOG(INTERNAL_FATAL) << "Unexpectedly returned from abort hook!";
+    LOG(FATAL_WITHOUT_ABORT) << "Unexpectedly returned from abort hook!";
   }
 
 #if defined(__GLIBC__)
@@ -528,7 +530,7 @@
 
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> class_loader_class(
-      hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader)));
+      hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader)));
   CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
 
   ArtMethod* getSystemClassLoader = class_loader_class->FindDirectMethod(
@@ -543,7 +545,7 @@
   soa.Self()->SetClassLoaderOverride(system_class_loader.get());
 
   Handle<mirror::Class> thread_class(
-      hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread)));
+      hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread)));
   CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
 
   ArtField* contextClassLoader =
@@ -551,8 +553,9 @@
   CHECK(contextClassLoader != nullptr);
 
   // We can't run in a transaction yet.
-  contextClassLoader->SetObject<false>(soa.Self()->GetPeer(),
-                                       soa.Decode<mirror::ClassLoader*>(system_class_loader.get()));
+  contextClassLoader->SetObject<false>(
+      soa.Self()->GetPeer(),
+      soa.Decode<mirror::ClassLoader>(system_class_loader.get()).Decode());
 
   return env->NewGlobalRef(system_class_loader.get());
 }
@@ -856,9 +859,9 @@
       const OatHeader& boot_oat_header = oat_file->GetOatHeader();
       const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
       if (boot_cp != nullptr) {
-        gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0],
-                                                         boot_cp,
-                                                         &image_locations);
+        gc::space::ImageSpace::ExtractMultiImageLocations(image_locations[0],
+                                                          boot_cp,
+                                                          &image_locations);
       }
     }
 
@@ -1378,9 +1381,9 @@
   register_java_lang_DexCache(env);
   register_java_lang_Object(env);
   register_java_lang_ref_FinalizerReference(env);
-  register_java_lang_reflect_AbstractMethod(env);
   register_java_lang_reflect_Array(env);
   register_java_lang_reflect_Constructor(env);
+  register_java_lang_reflect_Executable(env);
   register_java_lang_reflect_Field(env);
   register_java_lang_reflect_Method(env);
   register_java_lang_reflect_Parameter(env);
@@ -1547,6 +1550,8 @@
   mirror::String::VisitRoots(visitor);
   mirror::Throwable::VisitRoots(visitor);
   mirror::Field::VisitRoots(visitor);
+  mirror::MethodType::VisitRoots(visitor);
+  mirror::MethodHandleImpl::VisitRoots(visitor);
   // Visit all the primitive array types classes.
   mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor);   // BooleanArray
   mirror::PrimitiveArray<int8_t>::VisitRoots(visitor);    // ByteArray
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9e63564..30f1b4a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -314,6 +314,10 @@
     return "2.1.0";
   }
 
+  bool IsMethodHandlesEnabled() const {
+    return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+  }
+
   void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
   void AllowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
   void BroadcastForNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index 33600dd..aed6a2b 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -34,7 +34,10 @@
 void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
   static bool handling_unexpected_signal = false;
   if (handling_unexpected_signal) {
-    LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
+    LogHelper::LogLineLowStack(__FILE__,
+                               __LINE__,
+                               ::android::base::FATAL_WITHOUT_ABORT,
+                               "HandleUnexpectedSignal reentered\n");
     _exit(1);
   }
   handling_unexpected_signal = true;
@@ -44,11 +47,11 @@
   Runtime* runtime = Runtime::Current();
   if (runtime != nullptr) {
     // Print this out first in case DumpObject faults.
-    LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
+    LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
     gc::Heap* heap = runtime->GetHeap();
     if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
-      LOG(INTERNAL_FATAL) << "Dump heap object at fault address: ";
-      heap->DumpObject(LOG(INTERNAL_FATAL), reinterpret_cast<mirror::Object*>(info->si_addr));
+      LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
+      heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
     }
   }
   // Run the old signal handler.
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 60ebabc..cee73e1 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -309,7 +309,10 @@
 void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
   static bool handlingUnexpectedSignal = false;
   if (handlingUnexpectedSignal) {
-    LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
+    LogHelper::LogLineLowStack(__FILE__,
+                               __LINE__,
+                               ::android::base::FATAL_WITHOUT_ABORT,
+                               "HandleUnexpectedSignal reentered\n");
     if (IsTimeoutSignal(signal_number)) {
       // Ignore a recursive timeout.
       return;
@@ -334,7 +337,7 @@
   UContext thread_context(raw_context);
   Backtrace thread_backtrace(raw_context);
 
-  LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
+  LOG(FATAL_WITHOUT_ABORT) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
                       << StringPrintf("Fatal signal %d (%s), code %d (%s)",
                                       signal_number, GetSignalName(signal_number),
                                       info->si_code,
@@ -346,7 +349,7 @@
                       << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
                       << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
   if (kIsDebugBuild && signal_number == SIGSEGV) {
-    PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL);
+    PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
   }
   Runtime* runtime = Runtime::Current();
   if (runtime != nullptr) {
@@ -354,17 +357,17 @@
       // Special timeout signal. Try to dump all threads.
       // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
       //       are of value here.
-      runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout);
+      runtime->GetThreadList()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpNativeStackOnTimeout);
     }
     gc::Heap* heap = runtime->GetHeap();
-    LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
+    LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
     if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
-      LOG(INTERNAL_FATAL) << "Dump heap object at fault address: ";
-      heap->DumpObject(LOG(INTERNAL_FATAL), reinterpret_cast<mirror::Object*>(info->si_addr));
+      LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
+      heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
     }
   }
   if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) {
-    LOG(INTERNAL_FATAL) << "********************************************************\n"
+    LOG(FATAL_WITHOUT_ABORT) << "********************************************************\n"
                         << "* Process " << getpid() << " thread " << tid << " \"" << thread_name
                         << "\""
                         << " has been suspended while crashing.\n"
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 146afc7..b01a570 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,7 @@
 RUNTIME_OPTIONS_KEY (Unit,                NoDexFileFallback)
 RUNTIME_OPTIONS_KEY (std::string,         CpuAbiList)
 RUNTIME_OPTIONS_KEY (std::string,         Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents, method-handles}
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentLib)  // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentPath)  // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
 RUNTIME_OPTIONS_KEY (std::vector<Plugin>,            Plugins)  // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
new file mode 100644
index 0000000..cf020d0
--- /dev/null
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -0,0 +1,156 @@
+/*
+ * 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 ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_INL_H_
+#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_INL_H_
+
+#include "scoped_thread_state_change.h"
+
+#include "jni_env_ext-inl.h"
+#include "obj_ptr-inl.h"
+#include "thread-inl.h"
+
+namespace art {
+
+inline ScopedThreadStateChange::ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
+    : self_(self), thread_state_(new_thread_state), expected_has_no_thread_(false) {
+  if (UNLIKELY(self_ == nullptr)) {
+    // Value chosen arbitrarily and won't be used in the destructor since thread_ == null.
+    old_thread_state_ = kTerminated;
+    Runtime* runtime = Runtime::Current();
+    CHECK(runtime == nullptr || !runtime->IsStarted() || runtime->IsShuttingDown(self_));
+  } else {
+    DCHECK_EQ(self, Thread::Current());
+    // Read state without locks, ok as state is effectively thread local and we're not interested
+    // in the suspend count (this will be handled in the runnable transitions).
+    old_thread_state_ = self->GetState();
+    if (old_thread_state_ != new_thread_state) {
+      if (new_thread_state == kRunnable) {
+        self_->TransitionFromSuspendedToRunnable();
+      } else if (old_thread_state_ == kRunnable) {
+        self_->TransitionFromRunnableToSuspended(new_thread_state);
+      } else {
+        // A suspended transition to another effectively suspended transition, ok to use Unsafe.
+        self_->SetState(new_thread_state);
+      }
+    }
+  }
+}
+
+inline ScopedThreadStateChange::~ScopedThreadStateChange() {
+  if (UNLIKELY(self_ == nullptr)) {
+    if (!expected_has_no_thread_) {
+      Runtime* runtime = Runtime::Current();
+      bool shutting_down = (runtime == nullptr) || runtime->IsShuttingDown(nullptr);
+      CHECK(shutting_down);
+    }
+  } else {
+    if (old_thread_state_ != thread_state_) {
+      if (old_thread_state_ == kRunnable) {
+        self_->TransitionFromSuspendedToRunnable();
+      } else if (thread_state_ == kRunnable) {
+        self_->TransitionFromRunnableToSuspended(old_thread_state_);
+      } else {
+        // A suspended transition to another effectively suspended transition, ok to use Unsafe.
+        self_->SetState(old_thread_state_);
+      }
+    }
+  }
+}
+
+template<typename T>
+inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(mirror::Object* obj) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal());
+  return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj);
+}
+
+template<typename T, typename MirrorType, bool kPoison>
+inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(
+    ObjPtr<MirrorType, kPoison> obj) const {
+  return AddLocalReference<T>(obj.Decode());
+}
+
+template<typename T, bool kPoison>
+inline ObjPtr<T, kPoison> ScopedObjectAccessAlreadyRunnable::Decode(jobject obj) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  return down_cast<T*>(Self()->DecodeJObject(obj));
+}
+
+inline ArtField* ScopedObjectAccessAlreadyRunnable::DecodeField(jfieldID fid) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  return reinterpret_cast<ArtField*>(fid);
+}
+
+inline jfieldID ScopedObjectAccessAlreadyRunnable::EncodeField(ArtField* field) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  return reinterpret_cast<jfieldID>(field);
+}
+
+inline ArtMethod* ScopedObjectAccessAlreadyRunnable::DecodeMethod(jmethodID mid) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  return reinterpret_cast<ArtMethod*>(mid);
+}
+
+inline jmethodID ScopedObjectAccessAlreadyRunnable::EncodeMethod(ArtMethod* method) const {
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+  DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
+  return reinterpret_cast<jmethodID>(method);
+}
+
+inline bool ScopedObjectAccessAlreadyRunnable::IsRunnable() const {
+  return self_->GetState() == kRunnable;
+}
+
+inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
+    : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {}
+
+inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self)
+    : self_(self),
+      env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
+      vm_(env_ != nullptr ? env_->vm : nullptr) {}
+
+inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(JNIEnv* env)
+    : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) {
+  Self()->VerifyStack();
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+}
+
+inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(Thread* self)
+    : ScopedObjectAccessAlreadyRunnable(self), tsc_(self, kRunnable) {
+  Self()->VerifyStack();
+  Locks::mutator_lock_->AssertSharedHeld(Self());
+}
+
+inline ScopedThreadSuspension::ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
+    : self_(self), suspended_state_(suspended_state) {
+  DCHECK(self_ != nullptr);
+  self_->TransitionFromRunnableToSuspended(suspended_state);
+}
+
+inline ScopedThreadSuspension::~ScopedThreadSuspension() {
+  DCHECK_EQ(self_->GetState(), suspended_state_);
+  self_->TransitionFromSuspendedToRunnable();
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_INL_H_
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 8a1aca5..175bec5 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,85 +17,43 @@
 #ifndef ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
 #define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
 
-#include "base/casts.h"
-#include "java_vm_ext.h"
-#include "jni_env_ext-inl.h"
 #include "art_field.h"
-#include "read_barrier.h"
-#include "thread-inl.h"
+#include "base/casts.h"
+#include "base/value_object.h"
+#include "java_vm_ext.h"
+#include "thread_state.h"
 #include "verify_object.h"
 
 namespace art {
 
+struct JNIEnvExt;
+template<class MirrorType, bool kPoison> class ObjPtr;
+
 // Scoped change into and out of a particular state. Handles Runnable transitions that require
 // more complicated suspension checking. The subclasses ScopedObjectAccessUnchecked and
 // ScopedObjectAccess are used to handle the change into Runnable to Get direct access to objects,
 // the unchecked variant doesn't aid annotalysis.
 class ScopedThreadStateChange : public ValueObject {
  public:
-  ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
-      REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
-      : self_(self), thread_state_(new_thread_state), expected_has_no_thread_(false) {
-    if (UNLIKELY(self_ == nullptr)) {
-      // Value chosen arbitrarily and won't be used in the destructor since thread_ == null.
-      old_thread_state_ = kTerminated;
-      Runtime* runtime = Runtime::Current();
-      CHECK(runtime == nullptr || !runtime->IsStarted() || runtime->IsShuttingDown(self_));
-    } else {
-      DCHECK_EQ(self, Thread::Current());
-      // Read state without locks, ok as state is effectively thread local and we're not interested
-      // in the suspend count (this will be handled in the runnable transitions).
-      old_thread_state_ = self->GetState();
-      if (old_thread_state_ != new_thread_state) {
-        if (new_thread_state == kRunnable) {
-          self_->TransitionFromSuspendedToRunnable();
-        } else if (old_thread_state_ == kRunnable) {
-          self_->TransitionFromRunnableToSuspended(new_thread_state);
-        } else {
-          // A suspended transition to another effectively suspended transition, ok to use Unsafe.
-          self_->SetState(new_thread_state);
-        }
-      }
-    }
-  }
+  ALWAYS_INLINE ScopedThreadStateChange(Thread* self, ThreadState new_thread_state)
+      REQUIRES(!Locks::thread_suspend_count_lock_);
 
-  ~ScopedThreadStateChange() REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE {
-    if (UNLIKELY(self_ == nullptr)) {
-      if (!expected_has_no_thread_) {
-        Runtime* runtime = Runtime::Current();
-        bool shutting_down = (runtime == nullptr) || runtime->IsShuttingDown(nullptr);
-        CHECK(shutting_down);
-      }
-    } else {
-      if (old_thread_state_ != thread_state_) {
-        if (old_thread_state_ == kRunnable) {
-          self_->TransitionFromSuspendedToRunnable();
-        } else if (thread_state_ == kRunnable) {
-          self_->TransitionFromRunnableToSuspended(old_thread_state_);
-        } else {
-          // A suspended transition to another effectively suspended transition, ok to use Unsafe.
-          self_->SetState(old_thread_state_);
-        }
-      }
-    }
-  }
+  ALWAYS_INLINE ~ScopedThreadStateChange() REQUIRES(!Locks::thread_suspend_count_lock_);
 
-  Thread* Self() const {
+  ALWAYS_INLINE Thread* Self() const {
     return self_;
   }
 
  protected:
   // Constructor used by ScopedJniThreadState for an unattached thread that has access to the VM*.
-  ScopedThreadStateChange()
-      : self_(nullptr), thread_state_(kTerminated), old_thread_state_(kTerminated),
-        expected_has_no_thread_(true) {}
+  ScopedThreadStateChange() {}
 
-  Thread* const self_;
-  const ThreadState thread_state_;
+  Thread* const self_ = nullptr;
+  const ThreadState thread_state_ = kTerminated;
 
  private:
-  ThreadState old_thread_state_;
-  const bool expected_has_no_thread_;
+  ThreadState old_thread_state_ = kTerminated;
+  const bool expected_has_no_thread_ = true;
 
   friend class ScopedObjectAccessUnchecked;
   DISALLOW_COPY_AND_ASSIGN(ScopedThreadStateChange);
@@ -129,62 +87,34 @@
    * 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.
    */
+  template<typename T, typename MirrorType, bool kPoison = kIsDebugBuild>
+  T AddLocalReference(ObjPtr<MirrorType, kPoison> obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // TODO: Delete
   template<typename T>
-  T AddLocalReference(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal());
-    return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj);
-  }
+  T AddLocalReference(mirror::Object* obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<typename T>
-  T Decode(jobject obj) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    return down_cast<T>(Self()->DecodeJObject(obj));
-  }
+  template<typename T, bool kPoison = kIsDebugBuild>
+  ObjPtr<T, kPoison> Decode(jobject obj) const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtField* DecodeField(jfieldID fid) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    return reinterpret_cast<ArtField*>(fid);
-  }
+  ArtField* DecodeField(jfieldID fid) const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  jfieldID EncodeField(ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    return reinterpret_cast<jfieldID>(field);
-  }
+  jfieldID EncodeField(ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* DecodeMethod(jmethodID mid) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    return reinterpret_cast<ArtMethod*>(mid);
-  }
+  ArtMethod* DecodeMethod(jmethodID mid) const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  jmethodID EncodeMethod(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-    DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
-    return reinterpret_cast<jmethodID>(method);
-  }
+  jmethodID EncodeMethod(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool IsRunnable() const {
-    return self_->GetState() == kRunnable;
-  }
+  ALWAYS_INLINE bool IsRunnable() const;
 
  protected:
-  explicit ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
-      REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
-      : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
-  }
+  ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
+      REQUIRES(!Locks::thread_suspend_count_lock_);
 
-  explicit ScopedObjectAccessAlreadyRunnable(Thread* self)
-      REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
-      : self_(self), env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
-        vm_(env_ != nullptr ? env_->vm : nullptr) {
-  }
+  ALWAYS_INLINE explicit ScopedObjectAccessAlreadyRunnable(Thread* self)
+      REQUIRES(!Locks::thread_suspend_count_lock_);
 
   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
   // change into Runnable or acquire a share on the mutator_lock_.
@@ -192,8 +122,7 @@
       : self_(nullptr), env_(nullptr), vm_(down_cast<JavaVMExt*>(vm)) {}
 
   // Here purely to force inlining.
-  ~ScopedObjectAccessAlreadyRunnable() ALWAYS_INLINE {
-  }
+  ALWAYS_INLINE ~ScopedObjectAccessAlreadyRunnable() {}
 
   // Self thread, can be null.
   Thread* const self_;
@@ -219,19 +148,11 @@
 // the mutator_lock_ will be acquired on construction.
 class ScopedObjectAccessUnchecked : public ScopedObjectAccessAlreadyRunnable {
  public:
-  explicit ScopedObjectAccessUnchecked(JNIEnv* env)
-      REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
-      : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) {
-    Self()->VerifyStack();
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-  }
+  ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(JNIEnv* env)
+      REQUIRES(!Locks::thread_suspend_count_lock_);
 
-  explicit ScopedObjectAccessUnchecked(Thread* self)
-      REQUIRES(!Locks::thread_suspend_count_lock_) ALWAYS_INLINE
-      : ScopedObjectAccessAlreadyRunnable(self), tsc_(self, kRunnable) {
-    Self()->VerifyStack();
-    Locks::mutator_lock_->AssertSharedHeld(Self());
-  }
+  ALWAYS_INLINE explicit ScopedObjectAccessUnchecked(Thread* self)
+      REQUIRES(!Locks::thread_suspend_count_lock_);
 
   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
   // change into Runnable or acquire a share on the mutator_lock_.
@@ -249,28 +170,24 @@
 // Annotalysis helping variant of the above.
 class ScopedObjectAccess : public ScopedObjectAccessUnchecked {
  public:
-  explicit ScopedObjectAccess(JNIEnv* env)
+  ALWAYS_INLINE explicit ScopedObjectAccess(JNIEnv* env)
       REQUIRES(!Locks::thread_suspend_count_lock_)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
-      : ScopedObjectAccessUnchecked(env) {
-  }
+      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
+      : ScopedObjectAccessUnchecked(env) {}
 
-  explicit ScopedObjectAccess(Thread* self)
+  ALWAYS_INLINE explicit ScopedObjectAccess(Thread* self)
       REQUIRES(!Locks::thread_suspend_count_lock_)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
-      : ScopedObjectAccessUnchecked(self) {
-  }
+      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
+      : ScopedObjectAccessUnchecked(self) {}
 
-  ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
-    // Base class will release share of lock. Invoked after this destructor.
-  }
+  // Base class will release share of lock. Invoked after this destructor.
+  ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {}
 
  private:
   // TODO: remove this constructor. It is used by check JNI's ScopedCheck to make it believe that
   //       routines operating with just a VM are sound, they are not, but when you have just a VM
   //       you cannot call the unsound routines.
-  explicit ScopedObjectAccess(JavaVM* vm)
-      SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
+  explicit ScopedObjectAccess(JavaVM* vm) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
       : ScopedObjectAccessUnchecked(vm) {}
 
   friend class ScopedCheck;
@@ -280,19 +197,11 @@
 // Annotalysis helper for going to a suspended state from runnable.
 class ScopedThreadSuspension : public ValueObject {
  public:
-  explicit ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
+  ALWAYS_INLINE explicit ScopedThreadSuspension(Thread* self, ThreadState suspended_state)
       REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
-      UNLOCK_FUNCTION(Locks::mutator_lock_)
-      ALWAYS_INLINE
-      : self_(self), suspended_state_(suspended_state) {
-    DCHECK(self_ != nullptr);
-    self_->TransitionFromRunnableToSuspended(suspended_state);
-  }
+      UNLOCK_FUNCTION(Locks::mutator_lock_);
 
-  ~ScopedThreadSuspension() SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
-    DCHECK_EQ(self_->GetState(), suspended_state_);
-    self_->TransitionFromSuspendedToRunnable();
-  }
+  ALWAYS_INLINE ~ScopedThreadSuspension() SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
 
  private:
   Thread* const self_;
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 6cb7950..674459d 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -34,7 +34,7 @@
 #include "gc/heap.h"
 #include "os.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "signal_set.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -172,7 +172,7 @@
     LOG(INFO) << *self << ": reacting to signal " << signal_number;
 
     // If anyone's holding locks (which might prevent us from getting back into state Runnable), say so...
-    Runtime::Current()->DumpLockHolders(LOG(INFO));
+    Runtime::Current()->DumpLockHolders(LOG_STREAM(INFO));
   }
 
   return signal_number;
diff --git a/runtime/simulator/Android.bp b/runtime/simulator/Android.bp
index 49322fc..03e3f15 100644
--- a/runtime/simulator/Android.bp
+++ b/runtime/simulator/Android.bp
@@ -25,6 +25,7 @@
         "code_simulator_arm64.cc",
     ],
     shared_libs: [
+        "libbase",
         "liblog",
     ],
     cflags: ["-DVIXL_INCLUDE_SIMULATOR_AARCH64"],
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 216d8a7..bb6eb79 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -59,6 +59,9 @@
   if (UNLIKELY(TestAllFlags())) {
     CheckSuspend();
   }
+  // Invalidate the current thread's object pointers (ObjPtr) to catch possible moving GC bugs due
+  // to missing handles.
+  PoisonObjectPointers();
 }
 
 inline void Thread::CheckSuspend() {
@@ -172,6 +175,9 @@
 
 inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
   AssertThreadSuspensionIsAllowable();
+  if (kIsDebugBuild) {
+    PoisonObjectPointers();
+  }
   DCHECK_EQ(this, Thread::Current());
   // Change to non-runnable state, thereby appearing suspended to the system.
   TransitionToSuspendedAndRunCheckpoints(new_state);
@@ -302,6 +308,12 @@
   tlsPtr_.thread_local_alloc_stack_top = nullptr;
 }
 
+inline void Thread::PoisonObjectPointersIfDebug() {
+  if (kIsDebugBuild) {
+    Thread::Current()->PoisonObjectPointers();
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_THREAD_INL_H_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 8940354..ec1bb3f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -59,12 +59,13 @@
 #include "native_stack_dump.h"
 #include "nth_caller_visitor.h"
 #include "oat_quick_method_header.h"
+#include "obj_ptr-inl.h"
 #include "object_lock.h"
 #include "quick_exception_handler.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "stack.h"
@@ -127,43 +128,6 @@
   InitEntryPoints(&tlsPtr_.jni_entrypoints, &tlsPtr_.quick_entrypoints);
 }
 
-void Thread::InitStringEntryPoints() {
-  ScopedObjectAccess soa(this);
-  QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
-  qpoints->pNewEmptyString = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newEmptyString));
-  qpoints->pNewStringFromBytes_B = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B));
-  qpoints->pNewStringFromBytes_BI = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BI));
-  qpoints->pNewStringFromBytes_BII = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BII));
-  qpoints->pNewStringFromBytes_BIII = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIII));
-  qpoints->pNewStringFromBytes_BIIString = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIIString));
-  qpoints->pNewStringFromBytes_BString = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BString));
-  qpoints->pNewStringFromBytes_BIICharset = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIICharset));
-  qpoints->pNewStringFromBytes_BCharset = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BCharset));
-  qpoints->pNewStringFromChars_C = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_C));
-  qpoints->pNewStringFromChars_CII = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_CII));
-  qpoints->pNewStringFromChars_IIC = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_IIC));
-  qpoints->pNewStringFromCodePoints = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromCodePoints));
-  qpoints->pNewStringFromString = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromString));
-  qpoints->pNewStringFromStringBuffer = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromStringBuffer));
-  qpoints->pNewStringFromStringBuilder = reinterpret_cast<void(*)()>(
-      soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromStringBuilder));
-}
-
 void Thread::ResetQuickAllocEntryPointsForThread() {
   ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints);
 }
@@ -443,7 +407,7 @@
 
     // Copy peer into self, deleting global reference when done.
     CHECK(self->tlsPtr_.jpeer != nullptr);
-    self->tlsPtr_.opeer = soa.Decode<mirror::Object*>(self->tlsPtr_.jpeer);
+    self->tlsPtr_.opeer = soa.Decode<mirror::Object>(self->tlsPtr_.jpeer).Decode();
     self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer);
     self->tlsPtr_.jpeer = nullptr;
     self->SetThreadName(self->GetThreadName(soa)->ToModifiedUtf8().c_str());
@@ -481,7 +445,7 @@
 
 Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
                                   jobject java_thread) {
-  return FromManagedThread(soa, soa.Decode<mirror::Object*>(java_thread));
+  return FromManagedThread(soa, soa.Decode<mirror::Object>(java_thread).Decode());
 }
 
 static size_t FixStackSize(size_t stack_size) {
@@ -600,7 +564,7 @@
 
     ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_name);
     mirror::String* java_name = reinterpret_cast<mirror::String*>(f->GetObject(
-        soa.Decode<mirror::Object*>(java_peer)));
+        soa.Decode<mirror::Object>(java_peer).Decode()));
     std::string thread_name;
     if (java_name != nullptr) {
       thread_name = java_name->ToModifiedUtf8();
@@ -609,7 +573,7 @@
     }
 
     VLOG(threads) << "Creating native thread for " << thread_name;
-    self->Dump(LOG(INFO));
+    self->Dump(LOG_STREAM(INFO));
   }
 
   Runtime* runtime = Runtime::Current();
@@ -804,7 +768,7 @@
       VLOG(threads) << "Attaching unnamed thread.";
     }
     ScopedObjectAccess soa(self);
-    self->Dump(LOG(INFO));
+    self->Dump(LOG_STREAM(INFO));
   }
 
   {
@@ -839,7 +803,7 @@
   }
   {
     ScopedObjectAccess soa(this);
-    tlsPtr_.opeer = soa.Decode<mirror::Object*>(peer.get());
+    tlsPtr_.opeer = soa.Decode<mirror::Object>(peer.get()).Decode();
   }
   env->CallNonvirtualVoidMethod(peer.get(),
                                 WellKnownClasses::java_lang_Thread,
@@ -881,9 +845,11 @@
   soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->
       SetBoolean<kTransactionActive>(tlsPtr_.opeer, thread_is_daemon);
   soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->
-      SetObject<kTransactionActive>(tlsPtr_.opeer, soa.Decode<mirror::Object*>(thread_group));
+      SetObject<kTransactionActive>(tlsPtr_.opeer,
+                                    soa.Decode<mirror::Object>(thread_group).Decode());
   soa.DecodeField(WellKnownClasses::java_lang_Thread_name)->
-      SetObject<kTransactionActive>(tlsPtr_.opeer, soa.Decode<mirror::Object*>(thread_name));
+      SetObject<kTransactionActive>(tlsPtr_.opeer,
+                                    soa.Decode<mirror::Object>(thread_name).Decode());
   soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->
       SetInt<kTransactionActive>(tlsPtr_.opeer, thread_priority);
 }
@@ -911,8 +877,10 @@
     + 4 * KB;
   if (read_stack_size <= min_stack) {
     // Note, as we know the stack is small, avoid operations that could use a lot of stack.
-    LogMessage::LogLineLowStack(__PRETTY_FUNCTION__, __LINE__, ERROR,
-                                "Attempt to attach a thread with a too-small stack");
+    LogHelper::LogLineLowStack(__PRETTY_FUNCTION__,
+                               __LINE__,
+                               ::android::base::ERROR,
+                               "Attempt to attach a thread with a too-small stack");
     return false;
   }
 
@@ -2158,7 +2126,7 @@
     int* stack_depth) {
   // Decode the internal stack trace into the depth, method trace and PC trace.
   // Subtract one for the methods and PC trace.
-  int32_t depth = soa.Decode<mirror::Array*>(internal)->GetLength() - 1;
+  int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
   DCHECK_GE(depth, 0);
 
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
@@ -2170,7 +2138,7 @@
     result = output_array;
     // ...adjusting the number of frames we'll write to not exceed the array length.
     const int32_t traces_length =
-        soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->GetLength();
+        soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(result)->GetLength();
     depth = std::min(depth, traces_length);
   } else {
     // Create java_trace array and place in local reference table
@@ -2188,7 +2156,7 @@
 
   for (int32_t i = 0; i < depth; ++i) {
     mirror::ObjectArray<mirror::Object>* decoded_traces =
-        soa.Decode<mirror::Object*>(internal)->AsObjectArray<mirror::Object>();
+        soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>();
     // Methods and dex PC trace is element 0.
     DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
     mirror::PointerArray* const method_trace =
@@ -2240,7 +2208,7 @@
       return nullptr;
     }
     // We are called from native: use non-transactional mode.
-    soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set<false>(i, obj);
+    soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>>(result)->Set<false>(i, obj);
   }
   return result;
 }
@@ -2382,7 +2350,7 @@
     ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
     tls32_.throwing_OutOfMemoryError = false;
   } else {
-    Dump(LOG(WARNING));  // The pre-allocated OOME has no stack, so help out and log one.
+    Dump(LOG_STREAM(WARNING));  // The pre-allocated OOME has no stack, so help out and log one.
     SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
   }
 }
@@ -2775,16 +2743,16 @@
           bool failed = false;
           if (!space->GetLiveBitmap()->Test(klass)) {
             failed = true;
-            LOG(INTERNAL_FATAL) << "Unmarked object in image " << *space;
+            LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image " << *space;
           } else if (!heap->GetLiveBitmap()->Test(klass)) {
             failed = true;
-            LOG(INTERNAL_FATAL) << "Unmarked object in image through live bitmap " << *space;
+            LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image through live bitmap " << *space;
           }
           if (failed) {
-            GetThread()->Dump(LOG(INTERNAL_FATAL));
-            space->AsImageSpace()->DumpSections(LOG(INTERNAL_FATAL));
-            LOG(INTERNAL_FATAL) << "Method@" << method->GetDexMethodIndex() << ":" << method
-                                << " klass@" << klass;
+            GetThread()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+            space->AsImageSpace()->DumpSections(LOG_STREAM(FATAL_WITHOUT_ABORT));
+            LOG(FATAL_WITHOUT_ABORT) << "Method@" << method->GetDexMethodIndex() << ":" << method
+                                     << " klass@" << klass;
             // Pretty info last in case it crashes.
             LOG(FATAL) << "Method " << PrettyMethod(method) << " klass " << PrettyClass(klass);
           }
@@ -2838,7 +2806,7 @@
           if (kIsDebugBuild && ref_addr == nullptr) {
             std::string thread_name;
             GetThread()->GetThreadName(thread_name);
-            LOG(INTERNAL_FATAL) << "On thread " << thread_name;
+            LOG(FATAL_WITHOUT_ABORT) << "On thread " << thread_name;
             DescribeStack(GetThread());
             LOG(FATAL) << "Found an unsaved callee-save register " << i << " (null GPRAddress) "
                        << "set in register_mask=" << register_mask << " at " << DescribeLocation();
@@ -2953,7 +2921,7 @@
     // However, we seem to have already extended to use the full stack.
     LOG(ERROR) << "Need to increase kStackOverflowReservedBytes (currently "
                << GetStackOverflowReservedBytes(kRuntimeISA) << ")?";
-    DumpStack(LOG(ERROR));
+    DumpStack(LOG_STREAM(ERROR));
     LOG(FATAL) << "Recursive stack overflow.";
   }
 
@@ -3079,11 +3047,10 @@
   interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
 }
 
-void Thread::SetException(mirror::Throwable* new_exception) {
+void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
   CHECK(new_exception != nullptr);
   // TODO: DCHECK(!IsExceptionPending());
-  tlsPtr_.exception = new_exception;
-  // LOG(ERROR) << new_exception->Dump();
+  tlsPtr_.exception = new_exception.Decode();
 }
 
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 016c2bc..f2c22d1 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -366,7 +366,7 @@
   void AssertNoPendingException() const;
   void AssertNoPendingExceptionForNewException(const char* msg) const;
 
-  void SetException(mirror::Throwable* new_exception) REQUIRES_SHARED(Locks::mutator_lock_);
+  void SetException(ObjPtr<mirror::Throwable> new_exception) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void ClearException() REQUIRES_SHARED(Locks::mutator_lock_) {
     tlsPtr_.exception = nullptr;
@@ -471,6 +471,16 @@
   }
   void Notify() REQUIRES(!*wait_mutex_);
 
+  ALWAYS_INLINE void PoisonObjectPointers() {
+    ++poison_object_cookie_;
+  }
+
+  ALWAYS_INLINE static void PoisonObjectPointersIfDebug();
+
+  ALWAYS_INLINE uintptr_t GetPoisonObjectCookie() const {
+    return poison_object_cookie_;
+  }
+
  private:
   void NotifyLocked(Thread* self) REQUIRES(wait_mutex_);
 
@@ -892,7 +902,9 @@
 
   // Returns the fake exception used to activate deoptimization.
   static mirror::Throwable* GetDeoptimizationException() {
-    return reinterpret_cast<mirror::Throwable*>(-1);
+    // Note that the mirror::Throwable must be aligned to kObjectAlignment or else it cannot be
+    // represented by ObjPtr.
+    return reinterpret_cast<mirror::Throwable*>(0x100);
   }
 
   // Currently deoptimization invokes verifier which can trigger class loading
@@ -1528,6 +1540,9 @@
   // Debug disable read barrier count, only is checked for debug builds and only in the runtime.
   uint8_t debug_disallow_read_barrier_ = 0;
 
+  // Note that it is not in the packed struct, may not be accessed for cross compilation.
+  uintptr_t poison_object_cookie_ = 0;
+
   // Pending extra checkpoints if checkpoint_function_ is already used.
   std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
 
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 5e6c8a4..e2aca6a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -36,7 +36,7 @@
 #include "lock_word.h"
 #include "monitor.h"
 #include "native_stack_dump.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "trace.h"
 #include "well_known_classes.h"
@@ -214,7 +214,7 @@
     bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kDumpWaitTimeout);
     if (timed_out) {
       // Avoid a recursive abort.
-      LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR)
+      LOG((kIsDebugBuild && (gAborting == 0)) ? ::android::base::FATAL : ::android::base::ERROR)
           << "Unexpected time out during dump checkpoint.";
     }
   }
@@ -441,8 +441,8 @@
       // runnable (both cases waiting inside Thread::TransitionFromSuspendedToRunnable), or waiting
       // for the thread flip to end at the JNI critical section entry (kWaitingForGcThreadFlip),
       ThreadState state = thread->GetState();
-      if (state == kWaitingForGcThreadFlip ||
-          thread->IsTransitioningToRunnable()) {
+      if ((state == kWaitingForGcThreadFlip || thread->IsTransitioningToRunnable()) &&
+          thread->GetSuspendCount() == 1) {
         // The thread will resume right after the broadcast.
         thread->ModifySuspendCount(self, -1, nullptr, false);
         ++runnable_thread_count;
@@ -628,7 +628,8 @@
         // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
         if ((errno != EAGAIN) && (errno != EINTR)) {
           if (errno == ETIMEDOUT) {
-            LOG(kIsDebugBuild ? FATAL : ERROR) << "Unexpected time out during suspend all.";
+            LOG(kIsDebugBuild ? ::android::base::FATAL : ::android::base::ERROR)
+                << "Unexpected time out during suspend all.";
           } else {
             PLOG(FATAL) << "futex wait failed for SuspendAllInternal()";
           }
@@ -775,7 +776,10 @@
           // ThreadList::WaitForOtherNonDaemonThreadsToExit.
           suspended_thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
         }
-        ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
+        ThreadSuspendByPeerWarning(self,
+                                   ::android::base::WARNING,
+                                    "No such thread for suspend",
+                                    peer);
         return nullptr;
       }
       if (!Contains(thread)) {
@@ -822,7 +826,10 @@
         }
         const uint64_t total_delay = NanoTime() - start_time;
         if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
-          ThreadSuspendByPeerWarning(self, FATAL, "Thread suspension timed out", peer);
+          ThreadSuspendByPeerWarning(self,
+                                     ::android::base::FATAL,
+                                     "Thread suspension timed out",
+                                     peer);
           if (suspended_thread != nullptr) {
             CHECK_EQ(suspended_thread, thread);
             suspended_thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
@@ -882,7 +889,9 @@
         CHECK(suspended_thread == nullptr) << "Suspended thread " << suspended_thread
             << " no longer in thread list";
         // There's a race in inflating a lock and the owner giving up ownership and then dying.
-        ThreadSuspendByThreadIdWarning(WARNING, "No such thread id for suspend", thread_id);
+        ThreadSuspendByThreadIdWarning(::android::base::WARNING,
+                                       "No such thread id for suspend",
+                                       thread_id);
         return nullptr;
       }
       VLOG(threads) << "SuspendThreadByThreadId found thread: " << *thread;
@@ -923,7 +932,9 @@
         }
         const uint64_t total_delay = NanoTime() - start_time;
         if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
-          ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id);
+          ThreadSuspendByThreadIdWarning(::android::base::WARNING,
+                                         "Thread suspension timed out",
+                                         thread_id);
           if (suspended_thread != nullptr) {
             thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
           }
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 23591c2..f846746 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -37,7 +37,7 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "os.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedLocalRef.h"
 #include "thread.h"
 #include "thread_list.h"
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 82e529c..77c2b76 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -22,7 +22,7 @@
 #include "common_runtime_test.h"
 #include "dex_file.h"
 #include "mirror/array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -36,7 +36,7 @@
     jobject jclass_loader = LoadDex("Transaction");
     StackHandleScope<2> hs(soa.Self());
     Handle<mirror::ClassLoader> class_loader(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
     ASSERT_TRUE(class_loader.Get() != nullptr);
 
     // Load and initialize java.lang.ExceptionInInitializerError and the exception class used
@@ -173,7 +173,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<4> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
@@ -271,7 +271,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<5> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
@@ -373,7 +373,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<4> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
@@ -490,7 +490,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
@@ -539,7 +539,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
@@ -563,7 +563,7 @@
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc
index ea4d8b5..ec38b41 100644
--- a/runtime/type_lookup_table_test.cc
+++ b/runtime/type_lookup_table_test.cc
@@ -19,7 +19,7 @@
 
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "type_lookup_table.h"
 #include "utf-inl.h"
 
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 6f10aaa..a40e313 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -37,8 +37,9 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/string.h"
 #include "oat_quick_method_header.h"
+#include "obj_ptr-inl.h"
 #include "os.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utf-inl.h"
 
 #if defined(__APPLE__)
@@ -52,6 +53,77 @@
 
 namespace art {
 
+static const uint8_t kBase64Map[256] = {
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
+  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
+  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
+    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  // NOLINT
+   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,  // NOLINT
+  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
+   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  // NOLINT
+   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,  // NOLINT
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255
+};
+
+uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+  std::vector<uint8_t> tmp;
+  uint32_t t = 0, y = 0;
+  int g = 3;
+  for (size_t i = 0; src[i] != '\0'; ++i) {
+    uint8_t c = kBase64Map[src[i] & 0xFF];
+    if (c == 255) continue;
+    // the final = symbols are read and used to trim the remaining bytes
+    if (c == 254) {
+      c = 0;
+      // prevent g < 0 which would potentially allow an overflow later
+      if (--g < 0) {
+        *dst_size = 0;
+        return nullptr;
+      }
+    } else if (g != 3) {
+      // we only allow = to be at the end
+      *dst_size = 0;
+      return nullptr;
+    }
+    t = (t << 6) | c;
+    if (++y == 4) {
+      tmp.push_back((t >> 16) & 255);
+      if (g > 1) {
+        tmp.push_back((t >> 8) & 255);
+      }
+      if (g > 2) {
+        tmp.push_back(t & 255);
+      }
+      y = t = 0;
+    }
+  }
+  if (y != 0) {
+    *dst_size = 0;
+    return nullptr;
+  }
+  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+  if (dst_size != nullptr) {
+    *dst_size = tmp.size();
+  } else {
+    *dst_size = 0;
+  }
+  std::copy(tmp.begin(), tmp.end(), dst.get());
+  return dst.release();
+}
+
 pid_t GetTid() {
 #if defined(__APPLE__)
   uint64_t owner;
@@ -199,14 +271,14 @@
   }
 }
 
-std::string PrettyDescriptor(mirror::String* java_descriptor) {
+std::string PrettyStringDescriptor(ObjPtr<mirror::String> java_descriptor) {
   if (java_descriptor == nullptr) {
     return "null";
   }
   return PrettyDescriptor(java_descriptor->ToModifiedUtf8().c_str());
 }
 
-std::string PrettyDescriptor(mirror::Class* klass) {
+std::string PrettyDescriptor(ObjPtr<mirror::Class> klass) {
   if (klass == nullptr) {
     return "null";
   }
@@ -385,7 +457,7 @@
   return result;
 }
 
-std::string PrettyTypeOf(mirror::Object* obj) {
+std::string PrettyTypeOf(ObjPtr<mirror::Object> obj) {
   if (obj == nullptr) {
     return "null";
   }
@@ -400,7 +472,7 @@
   return result;
 }
 
-std::string PrettyClass(mirror::Class* c) {
+std::string PrettyClass(ObjPtr<mirror::Class> c) {
   if (c == nullptr) {
     return "null";
   }
@@ -411,7 +483,7 @@
   return result;
 }
 
-std::string PrettyClassAndClassLoader(mirror::Class* c) {
+std::string PrettyClassAndClassLoader(ObjPtr<mirror::Class> c) {
   if (c == nullptr) {
     return "null";
   }
diff --git a/runtime/utils.h b/runtime/utils.h
index f3284e8..ea9e8f7 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -33,6 +33,7 @@
 #include "base/mutex.h"
 #include "base/stringpiece.h"
 #include "globals.h"
+#include "obj_ptr.h"
 #include "primitive.h"
 
 class BacktraceMap;
@@ -116,6 +117,8 @@
   return static_cast<typename std::make_unsigned<T>::type>(x);
 }
 
+uint8_t* DecodeBase64(const char* src, size_t* dst_size);
+
 std::string PrintableChar(uint16_t ch);
 
 // Returns an ASCII string corresponding to the given UTF-8 string.
@@ -133,10 +136,10 @@
 // Returns 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 PrettyDescriptor(mirror::String* descriptor)
+std::string PrettyStringDescriptor(ObjPtr<mirror::String> descriptor)
     REQUIRES_SHARED(Locks::mutator_lock_);
 std::string PrettyDescriptor(const char* descriptor);
-std::string PrettyDescriptor(mirror::Class* klass)
+std::string PrettyDescriptor(ObjPtr<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
 std::string PrettyDescriptor(Primitive::Type type);
 
@@ -156,7 +159,7 @@
 // So given an instance of 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 PrettyTypeOf(mirror::Object* obj)
+std::string PrettyTypeOf(ObjPtr<mirror::Object> obj)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Returns a human-readable form of the type at an index in the specified dex file.
@@ -165,11 +168,11 @@
 
 // Returns a human-readable form of the name of the given class.
 // Given String.class, the output would be "java.lang.Class<java.lang.String>".
-std::string PrettyClass(mirror::Class* c)
+std::string PrettyClass(ObjPtr<mirror::Class> c)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Returns a human-readable form of the name of the given class with its class loader.
-std::string PrettyClassAndClassLoader(mirror::Class* c)
+std::string PrettyClassAndClassLoader(ObjPtr<mirror::Class> c)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Returns a human-readable version of the Java part of the access flags, e.g., "private static "
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index a85d033..5ccd446 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -38,8 +38,10 @@
           RoundUp(methods_offset_ + MethodsSize(header.method_ids_size_), StringsAlignment())),
       fields_offset_(
           RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
+      method_types_offset_(
+          RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())),
       size_(
-          RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), Alignment())) {
+          RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
 }
 
 inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
@@ -118,6 +120,27 @@
   return static_cast<size_t>(pointer_size_);
 }
 
+inline size_t DexCacheArraysLayout::MethodTypeOffset(uint32_t proto_idx) const {
+  return strings_offset_
+      + ElementOffset(PointerSize::k64,
+                      proto_idx % mirror::DexCache::kDexCacheMethodTypeCacheSize);
+}
+
+inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const {
+  size_t cache_size = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+  if (num_elements < cache_size) {
+    cache_size = num_elements;
+  }
+
+  return ArraySize(PointerSize::k64, cache_size);
+}
+
+inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
+  static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
+                "alignof(MethodTypeDexCacheType) != 8");
+  return alignof(mirror::MethodTypeDexCacheType);
+}
+
 inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
   return static_cast<size_t>(element_size) * idx;
 }
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 20ffa90..e222b46 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -35,6 +35,7 @@
         methods_offset_(0u),
         strings_offset_(0u),
         fields_offset_(0u),
+        method_types_offset_(0u),
         size_(0u) {
   }
 
@@ -94,12 +95,23 @@
 
   size_t FieldsAlignment() const;
 
+  size_t MethodTypesOffset() const {
+    return method_types_offset_;
+  }
+
+  size_t MethodTypeOffset(uint32_t method_type_idx) const;
+
+  size_t MethodTypesSize(size_t num_elements) const;
+
+  size_t MethodTypesAlignment() const;
+
  private:
   static constexpr size_t types_offset_ = 0u;
   const PointerSize pointer_size_;  // Must be first for construction initialization order.
   const size_t methods_offset_;
   const size_t strings_offset_;
   const size_t fields_offset_;
+  const size_t method_types_offset_;
   const size_t size_;
 
   static size_t Alignment(PointerSize pointer_size);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 3ba20a4..ef42222 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -26,7 +26,7 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/string.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "handle_scope-inl.h"
 
 #include "base/memory_tool.h"
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index a71578b..9fbf875 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -34,7 +34,9 @@
   return (memcmp(version_, kVdexVersion, sizeof(kVdexVersion)) == 0);
 }
 
-VdexFile::Header::Header() {
+VdexFile::Header::Header(uint32_t dex_size, uint32_t verifier_deps_size)
+    : dex_size_(dex_size),
+      verifier_deps_size_(verifier_deps_size) {
   memcpy(magic_, kVdexMagic, sizeof(kVdexMagic));
   memcpy(version_, kVdexVersion, sizeof(kVdexVersion));
   DCHECK(IsMagicValid());
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 9215e52..6bea153 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -42,17 +42,22 @@
  public:
   struct Header {
    public:
-    Header();
+    Header(uint32_t dex_size, uint32_t verifier_deps_size);
 
     bool IsMagicValid() const;
     bool IsVersionValid() const;
 
+    uint32_t GetDexSize() const { return dex_size_; }
+    uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; }
+
    private:
     static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
     static constexpr uint8_t kVdexVersion[] = { '0', '0', '0', '\0' };
 
     uint8_t magic_[4];
     uint8_t version_[4];
+    uint32_t dex_size_;
+    uint32_t verifier_deps_size_;
   };
 
   static VdexFile* Open(const std::string& vdex_filename,
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index f1d3189..87b6dc3 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -46,7 +46,7 @@
 #include "reg_type-inl.h"
 #include "register_line-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "utils.h"
 #include "verifier_deps.h"
 #include "handle_scope-inl.h"
@@ -434,14 +434,15 @@
             severity = LogSeverity::WARNING;
             break;
           case HardFailLogMode::kLogInternalFatal:
-            severity = LogSeverity::INTERNAL_FATAL;
+            severity = LogSeverity::FATAL_WITHOUT_ABORT;
             break;
           default:
             LOG(FATAL) << "Unsupported log-level " << static_cast<uint32_t>(log_level);
             UNREACHABLE();
         }
-        verifier.DumpFailures(LOG(severity) << "Verification error in "
-                                            << PrettyMethod(method_idx, *dex_file) << "\n");
+        verifier.DumpFailures(LOG_STREAM(severity) << "Verification error in "
+                                                   << PrettyMethod(method_idx, *dex_file)
+                                                   << "\n");
       }
       if (hard_failure_msg != nullptr) {
         CHECK(!verifier.failure_messages_.empty());
@@ -3284,7 +3285,7 @@
       }
       break;
     // Note: the following instructions encode offsets derived from class linking.
-    // As such they use Class*/Field*/AbstractMethod* as these offsets only have
+    // As such they use Class*/Field*/Executable* as these offsets only have
     // meaning if the class linking and resolution were successful.
     case Instruction::IGET_QUICK:
       VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Integer(), true);
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 646987a..837ee2d 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -22,7 +22,7 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "dex_file.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "verifier_log_mode.h"
 
 namespace art {
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 3bc2acc..a84668b 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -27,7 +27,7 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "reg_type_cache-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 #include <limits>
 #include <sstream>
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index f2411b5..49dac26 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -24,7 +24,7 @@
 #include "common_runtime_test.h"
 #include "reg_type_cache-inl.h"
 #include "reg_type-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
diff --git a/runtime/verifier/verifier_deps_test.cc b/runtime/verifier/verifier_deps_test.cc
index bbaf59f..4533464 100644
--- a/runtime/verifier/verifier_deps_test.cc
+++ b/runtime/verifier/verifier_deps_test.cc
@@ -25,7 +25,7 @@
 #include "mirror/class_loader.h"
 #include "runtime.h"
 #include "thread.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 namespace verifier {
@@ -58,7 +58,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_) {
     StackHandleScope<1> hs(Thread::Current());
     Handle<mirror::ClassLoader> class_loader_handle(
-        hs.NewHandle(soa->Decode<mirror::ClassLoader*>(class_loader_)));
+        hs.NewHandle(soa->Decode<mirror::ClassLoader>(class_loader_)));
     mirror::Class* klass = class_linker_->FindClass(Thread::Current(),
                                                     name.c_str(),
                                                     class_loader_handle);
@@ -84,8 +84,8 @@
 
     SetVerifierDeps(dex_files);
 
-    mirror::ClassLoader* loader = soa->Decode<mirror::ClassLoader*>(class_loader_);
-    class_linker_->RegisterDexFile(*dex_file_, loader);
+    ObjPtr<mirror::ClassLoader> loader = soa->Decode<mirror::ClassLoader>(class_loader_);
+    class_linker_->RegisterDexFile(*dex_file_, loader.Decode());
 
     klass_Main_ = FindClassByName("LMain;", soa);
     CHECK(klass_Main_ != nullptr);
@@ -97,7 +97,7 @@
 
     StackHandleScope<2> hs(Thread::Current());
     Handle<mirror::ClassLoader> class_loader_handle(
-        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
     Handle<mirror::DexCache> dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache()));
 
     const DexFile::ClassDef* class_def = klass_Main_->GetClassDef();
diff --git a/runtime/verifier/verifier_log_mode.h b/runtime/verifier/verifier_log_mode.h
index 3744b9b..e83d174 100644
--- a/runtime/verifier/verifier_log_mode.h
+++ b/runtime/verifier/verifier_log_mode.h
@@ -24,7 +24,7 @@
   kLogNone,                               // Don't log hard failures at all.
   kLogVerbose,                            // Log with severity VERBOSE.
   kLogWarning,                            // Log with severity WARNING.
-  kLogInternalFatal,                      // Log with severity INTERNAL_FATAL
+  kLogInternalFatal,                      // Log with severity FATAL_WITHOUT_ABORT
 };
 
 }  // namespace verifier
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 5f5fbc8..4dcf58f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -21,10 +21,11 @@
 #include <sstream>
 
 #include "base/logging.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "mirror/class.h"
 #include "mirror/throwable.h"
 #include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -48,10 +49,12 @@
 jclass WellKnownClasses::java_lang_NoClassDefFoundError;
 jclass WellKnownClasses::java_lang_Object;
 jclass WellKnownClasses::java_lang_OutOfMemoryError;
-jclass WellKnownClasses::java_lang_reflect_AbstractMethod;
 jclass WellKnownClasses::java_lang_reflect_Constructor;
+jclass WellKnownClasses::java_lang_reflect_Executable;
 jclass WellKnownClasses::java_lang_reflect_Field;
 jclass WellKnownClasses::java_lang_reflect_Method;
+jclass WellKnownClasses::java_lang_reflect_Parameter;
+jclass WellKnownClasses::java_lang_reflect_Parameter__array;
 jclass WellKnownClasses::java_lang_reflect_Proxy;
 jclass WellKnownClasses::java_lang_RuntimeException;
 jclass WellKnownClasses::java_lang_StackOverflowError;
@@ -86,41 +89,10 @@
 jmethodID WellKnownClasses::java_lang_Long_valueOf;
 jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
 jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
+jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
 jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
 jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
 jmethodID WellKnownClasses::java_lang_Short_valueOf;
-jmethodID WellKnownClasses::java_lang_String_init;
-jmethodID WellKnownClasses::java_lang_String_init_B;
-jmethodID WellKnownClasses::java_lang_String_init_BI;
-jmethodID WellKnownClasses::java_lang_String_init_BII;
-jmethodID WellKnownClasses::java_lang_String_init_BIII;
-jmethodID WellKnownClasses::java_lang_String_init_BIIString;
-jmethodID WellKnownClasses::java_lang_String_init_BString;
-jmethodID WellKnownClasses::java_lang_String_init_BIICharset;
-jmethodID WellKnownClasses::java_lang_String_init_BCharset;
-jmethodID WellKnownClasses::java_lang_String_init_C;
-jmethodID WellKnownClasses::java_lang_String_init_CII;
-jmethodID WellKnownClasses::java_lang_String_init_IIC;
-jmethodID WellKnownClasses::java_lang_String_init_String;
-jmethodID WellKnownClasses::java_lang_String_init_StringBuffer;
-jmethodID WellKnownClasses::java_lang_String_init_III;
-jmethodID WellKnownClasses::java_lang_String_init_StringBuilder;
-jmethodID WellKnownClasses::java_lang_StringFactory_newEmptyString;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BI;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BII;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIII;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIIString;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BString;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIICharset;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BCharset;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_C;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_CII;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_IIC;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromString;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuffer;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromCodePoints;
-jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuilder;
 jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr;
 jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
 jmethodID WellKnownClasses::java_lang_Thread_init;
@@ -154,7 +126,7 @@
 jfieldID WellKnownClasses::java_lang_Throwable_stackTrace;
 jfieldID WellKnownClasses::java_lang_Throwable_stackState;
 jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
-jfieldID WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod;
+jfieldID WellKnownClasses::java_lang_reflect_Executable_artMethod;
 jfieldID WellKnownClasses::java_lang_reflect_Proxy_h;
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity;
 jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress;
@@ -182,7 +154,7 @@
   if (fid == nullptr) {
     ScopedObjectAccess soa(env);
     if (soa.Self()->IsExceptionPending()) {
-      LOG(INTERNAL_FATAL) << soa.Self()->GetException()->Dump();
+      LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
     }
     std::ostringstream os;
     WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
@@ -199,7 +171,7 @@
   if (mid == nullptr) {
     ScopedObjectAccess soa(env);
     if (soa.Self()->IsExceptionPending()) {
-      LOG(INTERNAL_FATAL) << soa.Self()->GetException()->Dump();
+      LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
     }
     std::ostringstream os;
     WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
@@ -215,6 +187,76 @@
                      StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
 }
 
+#define STRING_INIT_LIST(V) \
+  V(java_lang_String_init, "()V", newEmptyString, "newEmptyString", "()Ljava/lang/String;", NewEmptyString) \
+  V(java_lang_String_init_B, "([B)V", newStringFromBytes_B, "newStringFromBytes", "([B)Ljava/lang/String;", NewStringFromBytes_B) \
+  V(java_lang_String_init_BI, "([BI)V", newStringFromBytes_BI, "newStringFromBytes", "([BI)Ljava/lang/String;", NewStringFromBytes_BI) \
+  V(java_lang_String_init_BII, "([BII)V", newStringFromBytes_BII, "newStringFromBytes", "([BII)Ljava/lang/String;", NewStringFromBytes_BII) \
+  V(java_lang_String_init_BIII, "([BIII)V", newStringFromBytes_BIII, "newStringFromBytes", "([BIII)Ljava/lang/String;", NewStringFromBytes_BIII) \
+  V(java_lang_String_init_BIIString, "([BIILjava/lang/String;)V", newStringFromBytes_BIIString, "newStringFromBytes", "([BIILjava/lang/String;)Ljava/lang/String;", NewStringFromBytes_BIIString) \
+  V(java_lang_String_init_BString, "([BLjava/lang/String;)V", newStringFromBytes_BString, "newStringFromBytes", "([BLjava/lang/String;)Ljava/lang/String;", NewStringFromBytes_BString) \
+  V(java_lang_String_init_BIICharset, "([BIILjava/nio/charset/Charset;)V", newStringFromBytes_BIICharset, "newStringFromBytes", "([BIILjava/nio/charset/Charset;)Ljava/lang/String;", NewStringFromBytes_BIICharset) \
+  V(java_lang_String_init_BCharset, "([BLjava/nio/charset/Charset;)V", newStringFromBytes_BCharset, "newStringFromBytes", "([BLjava/nio/charset/Charset;)Ljava/lang/String;", NewStringFromBytes_BCharset) \
+  V(java_lang_String_init_C, "([C)V", newStringFromChars_C, "newStringFromChars", "([C)Ljava/lang/String;", NewStringFromChars_C) \
+  V(java_lang_String_init_CII, "([CII)V", newStringFromChars_CII, "newStringFromChars", "([CII)Ljava/lang/String;", NewStringFromChars_CII) \
+  V(java_lang_String_init_IIC, "(II[C)V", newStringFromChars_IIC, "newStringFromChars", "(II[C)Ljava/lang/String;", NewStringFromChars_IIC) \
+  V(java_lang_String_init_String, "(Ljava/lang/String;)V", newStringFromString, "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;", NewStringFromString) \
+  V(java_lang_String_init_StringBuffer, "(Ljava/lang/StringBuffer;)V", newStringFromStringBuffer, "newStringFromStringBuffer", "(Ljava/lang/StringBuffer;)Ljava/lang/String;", NewStringFromStringBuffer) \
+  V(java_lang_String_init_III, "([III)V", newStringFromCodePoints, "newStringFromCodePoints", "([III)Ljava/lang/String;", NewStringFromCodePoints) \
+  V(java_lang_String_init_StringBuilder, "(Ljava/lang/StringBuilder;)V", newStringFromStringBuilder, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;", NewStringFromStringBuilder) \
+
+#define STATIC_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, ...) \
+    static ArtMethod* init_runtime_name; \
+    static ArtMethod* new_runtime_name;
+    STRING_INIT_LIST(STATIC_STRING_INIT)
+#undef STATIC_STRING_INIT
+
+void WellKnownClasses::InitStringInit(JNIEnv* env) {
+  ScopedObjectAccess soa(Thread::Current());
+  #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name,             \
+                           new_java_name, new_signature, ...)                               \
+      init_runtime_name = soa.DecodeMethod(                                                 \
+          CacheMethod(env, java_lang_String, false, "<init>", init_signature));             \
+      new_runtime_name = soa.DecodeMethod(                                                  \
+          CacheMethod(env, java_lang_StringFactory, true, new_java_name, new_signature));
+      STRING_INIT_LIST(LOAD_STRING_INIT)
+  #undef LOAD_STRING_INIT
+}
+
+void Thread::InitStringEntryPoints() {
+  QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
+  #define SET_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name,              \
+                          new_java_name, new_signature, entry_point_name)                   \
+      qpoints->p ## entry_point_name = reinterpret_cast<void(*)()>(new_runtime_name);
+      STRING_INIT_LIST(SET_ENTRY_POINT)
+  #undef SET_ENTRY_POINT
+}
+
+ArtMethod* WellKnownClasses::StringInitToStringFactory(ArtMethod* string_init) {
+  #define TO_STRING_FACTORY(init_runtime_name, init_signature, new_runtime_name,            \
+                            new_java_name, new_signature, entry_point_name)                 \
+      if (string_init == init_runtime_name) {                                               \
+        return new_runtime_name;                                                            \
+      }
+      STRING_INIT_LIST(TO_STRING_FACTORY)
+  #undef TO_STRING_FACTORY
+  LOG(FATAL) << "Could not find StringFactory method for String.<init>";
+  return nullptr;
+}
+
+uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) {
+  #define TO_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name,               \
+                         new_java_name, new_signature, entry_point_name)                    \
+      if (string_init == init_runtime_name) {                                               \
+        return kQuick ## entry_point_name;                                                  \
+      }
+      STRING_INIT_LIST(TO_ENTRY_POINT)
+  #undef TO_STRING_FACTORY
+  LOG(FATAL) << "Could not find StringFactory method for String.<init>";
+  return 0;
+}
+#undef STRING_INIT_LIST
+
 void WellKnownClasses::Init(JNIEnv* env) {
   com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
   dalvik_annotation_optimization_CriticalNative =
@@ -237,10 +279,12 @@
   java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
   java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
   java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
-  java_lang_reflect_AbstractMethod = CacheClass(env, "java/lang/reflect/AbstractMethod");
   java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor");
+  java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable");
   java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field");
   java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method");
+  java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter");
+  java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;");
   java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
   java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
   java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
@@ -273,6 +317,7 @@
   ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue"));
   java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
 
+  java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
   java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
   java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
   java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
@@ -284,62 +329,6 @@
   org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
   org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
 
-  java_lang_String_init = CacheMethod(env, java_lang_String, false, "<init>", "()V");
-  java_lang_String_init_B = CacheMethod(env, java_lang_String, false, "<init>", "([B)V");
-  java_lang_String_init_BI = CacheMethod(env, java_lang_String, false, "<init>", "([BI)V");
-  java_lang_String_init_BII = CacheMethod(env, java_lang_String, false, "<init>", "([BII)V");
-  java_lang_String_init_BIII = CacheMethod(env, java_lang_String, false, "<init>", "([BIII)V");
-  java_lang_String_init_BIIString = CacheMethod(env, java_lang_String, false, "<init>",
-      "([BIILjava/lang/String;)V");
-  java_lang_String_init_BString = CacheMethod(env, java_lang_String, false, "<init>",
-      "([BLjava/lang/String;)V");
-  java_lang_String_init_BIICharset = CacheMethod(env, java_lang_String, false, "<init>",
-      "([BIILjava/nio/charset/Charset;)V");
-  java_lang_String_init_BCharset = CacheMethod(env, java_lang_String, false, "<init>",
-      "([BLjava/nio/charset/Charset;)V");
-  java_lang_String_init_C = CacheMethod(env, java_lang_String, false, "<init>", "([C)V");
-  java_lang_String_init_CII = CacheMethod(env, java_lang_String, false, "<init>", "([CII)V");
-  java_lang_String_init_IIC = CacheMethod(env, java_lang_String, false, "<init>", "(II[C)V");
-  java_lang_String_init_String = CacheMethod(env, java_lang_String, false, "<init>",
-      "(Ljava/lang/String;)V");
-  java_lang_String_init_StringBuffer = CacheMethod(env, java_lang_String, false, "<init>",
-      "(Ljava/lang/StringBuffer;)V");
-  java_lang_String_init_III = CacheMethod(env, java_lang_String, false, "<init>", "([III)V");
-  java_lang_String_init_StringBuilder = CacheMethod(env, java_lang_String, false, "<init>",
-       "(Ljava/lang/StringBuilder;)V");
-  java_lang_StringFactory_newEmptyString = CacheMethod(env, java_lang_StringFactory, true,
-       "newEmptyString", "()Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_B = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromBytes", "([B)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BI = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromBytes", "([BI)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BII = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromBytes", "([BII)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BIII = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromBytes", "([BIII)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BIIString = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromBytes", "([BIILjava/lang/String;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BString = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromBytes", "([BLjava/lang/String;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BIICharset = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromBytes", "([BIILjava/nio/charset/Charset;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromBytes_BCharset = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromBytes", "([BLjava/nio/charset/Charset;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromChars_C = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromChars", "([C)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromChars_CII = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromChars", "([CII)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromChars_IIC = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromChars", "(II[C)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromString = CacheMethod(env, java_lang_StringFactory, true,
-       "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromStringBuffer = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromStringBuffer", "(Ljava/lang/StringBuffer;)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromCodePoints = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromCodePoints", "([III)Ljava/lang/String;");
-  java_lang_StringFactory_newStringFromStringBuilder = CacheMethod(env, java_lang_StringFactory,
-       true, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;");
-
   dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
   dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
   dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
@@ -362,7 +351,7 @@
   java_lang_Throwable_stackTrace = CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
   java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
   java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
-  java_lang_reflect_AbstractMethod_artMethod = CacheField(env, java_lang_reflect_AbstractMethod, false, "artMethod", "J");
+  java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J");
   java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;");
   java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
   java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
@@ -384,6 +373,7 @@
   java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
   java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
 
+  InitStringInit(env);
   Thread::Current()->InitStringEntryPoints();
 }
 
@@ -399,43 +389,4 @@
   return reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(global_jclass));
 }
 
-jmethodID WellKnownClasses::StringInitToStringFactoryMethodID(jmethodID string_init) {
-  // TODO: Prioritize ordering.
-  if (string_init == java_lang_String_init) {
-    return java_lang_StringFactory_newEmptyString;
-  } else if (string_init == java_lang_String_init_B) {
-    return java_lang_StringFactory_newStringFromBytes_B;
-  } else if (string_init == java_lang_String_init_BI) {
-    return java_lang_StringFactory_newStringFromBytes_BI;
-  } else if (string_init == java_lang_String_init_BII) {
-    return java_lang_StringFactory_newStringFromBytes_BII;
-  } else if (string_init == java_lang_String_init_BIII) {
-    return java_lang_StringFactory_newStringFromBytes_BIII;
-  } else if (string_init == java_lang_String_init_BIIString) {
-    return java_lang_StringFactory_newStringFromBytes_BIIString;
-  } else if (string_init == java_lang_String_init_BString) {
-    return java_lang_StringFactory_newStringFromBytes_BString;
-  } else if (string_init == java_lang_String_init_BIICharset) {
-    return java_lang_StringFactory_newStringFromBytes_BIICharset;
-  } else if (string_init == java_lang_String_init_BCharset) {
-    return java_lang_StringFactory_newStringFromBytes_BCharset;
-  } else if (string_init == java_lang_String_init_C) {
-    return java_lang_StringFactory_newStringFromChars_C;
-  } else if (string_init == java_lang_String_init_CII) {
-    return java_lang_StringFactory_newStringFromChars_CII;
-  } else if (string_init == java_lang_String_init_IIC) {
-    return java_lang_StringFactory_newStringFromChars_IIC;
-  } else if (string_init == java_lang_String_init_String) {
-    return java_lang_StringFactory_newStringFromString;
-  } else if (string_init == java_lang_String_init_StringBuffer) {
-    return java_lang_StringFactory_newStringFromStringBuffer;
-  } else if (string_init == java_lang_String_init_III) {
-    return java_lang_StringFactory_newStringFromCodePoints;
-  } else if (string_init == java_lang_String_init_StringBuilder) {
-    return java_lang_StringFactory_newStringFromStringBuilder;
-  }
-  LOG(FATAL) << "Could not find StringFactory method for String.<init>";
-  return nullptr;
-}
-
 }  // namespace art
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index ce710ff..ddfc5b8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -21,6 +21,9 @@
 #include "jni.h"
 
 namespace art {
+
+class ArtMethod;
+
 namespace mirror {
 class Class;
 }  // namespace mirror
@@ -35,7 +38,8 @@
  public:
   static void Init(JNIEnv* env);  // Run before native methods are registered.
   static void LateInit(JNIEnv* env);  // Run after native methods are registered.
-  static jmethodID StringInitToStringFactoryMethodID(jmethodID string_init);
+  static ArtMethod* StringInitToStringFactory(ArtMethod* method);
+  static uint32_t StringInitToEntryPoint(ArtMethod* method);
 
   static mirror::Class* ToClass(jclass global_jclass)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -59,10 +63,12 @@
   static jclass java_lang_NoClassDefFoundError;
   static jclass java_lang_Object;
   static jclass java_lang_OutOfMemoryError;
-  static jclass java_lang_reflect_AbstractMethod;
   static jclass java_lang_reflect_Constructor;
+  static jclass java_lang_reflect_Executable;
   static jclass java_lang_reflect_Field;
   static jclass java_lang_reflect_Method;
+  static jclass java_lang_reflect_Parameter;
+  static jclass java_lang_reflect_Parameter__array;
   static jclass java_lang_reflect_Proxy;
   static jclass java_lang_RuntimeException;
   static jclass java_lang_StackOverflowError;
@@ -97,41 +103,10 @@
   static jmethodID java_lang_Long_valueOf;
   static jmethodID java_lang_ref_FinalizerReference_add;
   static jmethodID java_lang_ref_ReferenceQueue_add;
+  static jmethodID java_lang_reflect_Parameter_init;
   static jmethodID java_lang_reflect_Proxy_invoke;
   static jmethodID java_lang_Runtime_nativeLoad;
   static jmethodID java_lang_Short_valueOf;
-  static jmethodID java_lang_String_init;
-  static jmethodID java_lang_String_init_B;
-  static jmethodID java_lang_String_init_BI;
-  static jmethodID java_lang_String_init_BII;
-  static jmethodID java_lang_String_init_BIII;
-  static jmethodID java_lang_String_init_BIIString;
-  static jmethodID java_lang_String_init_BString;
-  static jmethodID java_lang_String_init_BIICharset;
-  static jmethodID java_lang_String_init_BCharset;
-  static jmethodID java_lang_String_init_C;
-  static jmethodID java_lang_String_init_CII;
-  static jmethodID java_lang_String_init_IIC;
-  static jmethodID java_lang_String_init_String;
-  static jmethodID java_lang_String_init_StringBuffer;
-  static jmethodID java_lang_String_init_III;
-  static jmethodID java_lang_String_init_StringBuilder;
-  static jmethodID java_lang_StringFactory_newEmptyString;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_B;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BI;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BII;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BIII;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BIIString;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BString;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BIICharset;
-  static jmethodID java_lang_StringFactory_newStringFromBytes_BCharset;
-  static jmethodID java_lang_StringFactory_newStringFromChars_C;
-  static jmethodID java_lang_StringFactory_newStringFromChars_CII;
-  static jmethodID java_lang_StringFactory_newStringFromChars_IIC;
-  static jmethodID java_lang_StringFactory_newStringFromString;
-  static jmethodID java_lang_StringFactory_newStringFromStringBuffer;
-  static jmethodID java_lang_StringFactory_newStringFromCodePoints;
-  static jmethodID java_lang_StringFactory_newStringFromStringBuilder;
   static jmethodID java_lang_System_runFinalization;
   static jmethodID java_lang_Thread_dispatchUncaughtException;
   static jmethodID java_lang_Thread_init;
@@ -148,7 +123,7 @@
   static jfieldID dalvik_system_DexPathList_dexElements;
   static jfieldID dalvik_system_DexPathList__Element_dexFile;
   static jfieldID dalvik_system_PathClassLoader_pathList;
-  static jfieldID java_lang_reflect_AbstractMethod_artMethod;
+  static jfieldID java_lang_reflect_Executable_artMethod;
   static jfieldID java_lang_reflect_Proxy_h;
   static jfieldID java_lang_Thread_daemon;
   static jfieldID java_lang_Thread_group;
@@ -177,6 +152,9 @@
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type;
+
+ private:
+  static void InitStringInit(JNIEnv* env);
 };
 
 }  // namespace art
diff --git a/test/004-ThreadStress/thread_stress.cc b/test/004-ThreadStress/thread_stress.cc
index 573c352..8ae3dfb 100644
--- a/test/004-ThreadStress/thread_stress.cc
+++ b/test/004-ThreadStress/thread_stress.cc
@@ -19,18 +19,18 @@
 #include "jni.h"
 #include "mirror/string.h"
 #include "mirror/throwable.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
 extern "C" JNIEXPORT void JNICALL Java_Main_printString(JNIEnv*, jclass, jstring s) {
   ScopedObjectAccess soa(Thread::Current());
-  std::cout << soa.Decode<mirror::String*>(s)->ToModifiedUtf8();
+  std::cout << soa.Decode<mirror::String>(s)->ToModifiedUtf8();
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_printThrowable(JNIEnv*, jclass, jthrowable t) {
   ScopedObjectAccess soa(Thread::Current());
-  std::cout << soa.Decode<mirror::Throwable*>(t)->Dump();
+  std::cout << soa.Decode<mirror::Throwable>(t)->Dump();
 }
 
 }  // namespace art
diff --git a/test/004-UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc
index 3b0cf23..4f6ae5a 100644
--- a/test/004-UnsafeTest/unsafe_test.cc
+++ b/test/004-UnsafeTest/unsafe_test.cc
@@ -20,20 +20,20 @@
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_vmArrayBaseOffset(JNIEnv* env, jclass, jobject classObj) {
   ScopedObjectAccess soa(env);
-  mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(classObj);
   return mirror::Array::DataOffset(
       Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType())).Int32Value();
 }
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_vmArrayIndexScale(JNIEnv* env, jclass, jobject classObj) {
   ScopedObjectAccess soa(env);
-  mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(classObj);
   return Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType());
 }
 
diff --git a/test/005-annotations/build b/test/005-annotations/build
index 216843d..8b9f550 100644
--- a/test/005-annotations/build
+++ b/test/005-annotations/build
@@ -30,7 +30,8 @@
 
 if [ ${USE_JACK} = "true" ]; then
   jar cf classes.jill.jar -C classes .
-  ${JACK} --import classes.jill.jar --output-dex .
+  # Jack needs to emit annotations with CLASS retention.
+  ${JACK} -D jack.dex.annotation.class-retention=true --import classes.jill.jar --output-dex .
 else
   if [ ${NEED_DEX} = "true" ]; then
     ${DX} -JXmx256m --debug --dex --output=classes.dex classes
diff --git a/test/031-class-attributes/src/ClassAttrs.java b/test/031-class-attributes/src/ClassAttrs.java
index 346e13d..39e69a3 100644
--- a/test/031-class-attributes/src/ClassAttrs.java
+++ b/test/031-class-attributes/src/ClassAttrs.java
@@ -1,9 +1,9 @@
 import otherpackage.OtherPackageClass;
 
 import java.io.Serializable;
-import java.lang.reflect.AbstractMethod;
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -223,7 +223,7 @@
         try {
             Class<?> c = obj.getClass();
             if (c == Method.class || c == Constructor.class) {
-              c = AbstractMethod.class;
+              c = Executable.class;
             }
             method = c.getDeclaredMethod("getSignatureAttribute");
             method.setAccessible(true);
diff --git a/test/054-uncaught/src/Main.java b/test/054-uncaught/src/Main.java
index 90a2311..688a2a4 100644
--- a/test/054-uncaught/src/Main.java
+++ b/test/054-uncaught/src/Main.java
@@ -41,7 +41,7 @@
         ThreadDeathHandler defHandler = new ThreadDeathHandler("DEFAULT");
         ThreadDeathHandler threadHandler = new ThreadDeathHandler("THREAD");
 
-        System.out.println("Test " + which);
+        System.err.println("Test " + which);
         switch (which) {
             case 1: {
                 Thread.setDefaultUncaughtExceptionHandler(defHandler);
diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check
index 57111bc..d124ce8 100755
--- a/test/099-vmdebug/check
+++ b/test/099-vmdebug/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip the process pids and line numbers from exact error messages.
-sed -e '/^art E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index c6a2e9a..56c0dcd 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -20,7 +20,7 @@
 #include "gc/space/image_space.h"
 #include "mirror/class-inl.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 
 namespace art {
@@ -29,7 +29,7 @@
  public:
   static const OatFile::OatDexFile* getOatDexFile(jclass cls) {
     ScopedObjectAccess soa(Thread::Current());
-    mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+    ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
     const DexFile& dex_file = klass->GetDexFile();
     return dex_file.GetOatDexFile();
   }
diff --git a/test/118-noimage-dex2oat/check b/test/118-noimage-dex2oat/check
index 57111bc..4f46083 100755
--- a/test/118-noimage-dex2oat/check
+++ b/test/118-noimage-dex2oat/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip the process pids and line numbers from exact error messages.
-sed -e '/^art E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^dalvikvm.* E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/119-noimage-patchoat/check b/test/119-noimage-patchoat/check
index 57111bc..d124ce8 100755
--- a/test/119-noimage-patchoat/check
+++ b/test/119-noimage-patchoat/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip the process pids and line numbers from exact error messages.
-sed -e '/^art E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/1337-gc-coverage/gc_coverage.cc b/test/1337-gc-coverage/gc_coverage.cc
index 7cf30bd..1e60bd9 100644
--- a/test/1337-gc-coverage/gc_coverage.cc
+++ b/test/1337-gc-coverage/gc_coverage.cc
@@ -17,7 +17,7 @@
 #include "gc/heap.h"
 #include "jni.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -43,7 +43,7 @@
 
 extern "C" JNIEXPORT jlong JNICALL Java_Main_objectAddress(JNIEnv* env, jclass, jobject object) {
   ScopedObjectAccess soa(env);
-  return reinterpret_cast<jlong>(soa.Decode<mirror::Object*>(object));
+  return reinterpret_cast<jlong>(soa.Decode<mirror::Object>(object).Decode());
 }
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportCollectorTransition(JNIEnv*, jclass) {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 9f1499e..113b35f 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -91,7 +91,7 @@
 static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
   printf("Secondary pid is %d\n", pid);
 
-  PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ERROR);
+  PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
 
   if (sig_quit_on_fail) {
     int res = kill(pid, SIGQUIT);
diff --git a/test/143-string-value/check b/test/143-string-value/check
index 92f6e90..2a3476c 100755
--- a/test/143-string-value/check
+++ b/test/143-string-value/check
@@ -15,6 +15,6 @@
 # limitations under the License.
 
 # Strip error log messages.
-sed -e '/^art E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
 
 diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/148-multithread-gc-annotations/gc_coverage.cc b/test/148-multithread-gc-annotations/gc_coverage.cc
index 263eefd..cb12df4 100644
--- a/test/148-multithread-gc-annotations/gc_coverage.cc
+++ b/test/148-multithread-gc-annotations/gc_coverage.cc
@@ -17,7 +17,7 @@
 #include "gc/heap.h"
 #include "jni.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -35,7 +35,7 @@
 
 extern "C" JNIEXPORT jlong JNICALL Java_MovingGCThread_objectAddress(JNIEnv* env, jclass, jobject object) {
   ScopedObjectAccess soa(env);
-  return reinterpret_cast<jlong>(soa.Decode<mirror::Object*>(object));
+  return reinterpret_cast<jlong>(soa.Decode<mirror::Object>(object).Decode());
 }
 
 }  // namespace
diff --git a/test/149-suspend-all-stress/suspend_all.cc b/test/149-suspend-all-stress/suspend_all.cc
index dfd944a..c1c0ff9 100644
--- a/test/149-suspend-all-stress/suspend_all.cc
+++ b/test/149-suspend-all-stress/suspend_all.cc
@@ -42,14 +42,16 @@
         break;
       }
       case kOPDumpStack: {
-        Runtime::Current()->GetThreadList()->Dump(LOG(INFO));
+        Runtime::Current()->GetThreadList()->Dump(LOG_STREAM(INFO));
         usleep(500);
         break;
       }
       case kOPSuspendAllDumpStack: {
         // Not yet supported.
-        // ScopedSuspendAll ssa(__FUNCTION__);
-        // Runtime::Current()->GetThreadList()->Dump(LOG(INFO));
+        if ((false)) {
+          ScopedSuspendAll ssa(__FUNCTION__);
+          Runtime::Current()->GetThreadList()->Dump(LOG_STREAM(INFO));
+        }
         break;
       }
       case kOPNumber:
diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc
index 5762754..9058af4 100644
--- a/test/454-get-vreg/get_vreg_jni.cc
+++ b/test/454-get-vreg/get_vreg_jni.cc
@@ -18,7 +18,7 @@
 #include "art_method-inl.h"
 #include "jni.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
@@ -123,7 +123,7 @@
 extern "C" JNIEXPORT jint JNICALL Java_Main_doNativeCall(JNIEnv*, jobject value) {
   ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<Context> context(Context::Create());
-  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value));
+  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object>(value).Decode());
   visitor.WalkStack();
   return visitor.found_method_index_;
 }
diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc
index 08db775..f62a77d 100644
--- a/test/457-regs/regs_jni.cc
+++ b/test/457-regs/regs_jni.cc
@@ -18,7 +18,7 @@
 #include "art_method-inl.h"
 #include "jni.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
@@ -139,7 +139,7 @@
     JNIEnv*, jclass value ATTRIBUTE_UNUSED, jobject main, jint int_value, jfloat float_value) {
   ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<Context> context(Context::Create());
-  CHECK(soa.Decode<mirror::Object*>(main) == nullptr);
+  CHECK(soa.Decode<mirror::Object>(main) == nullptr);
   CHECK_EQ(int_value, 0);
   int32_t cast = bit_cast<int32_t, float>(float_value);
   CHECK_EQ(cast, 0);
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 5b14735..40baa15 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -1178,16 +1178,32 @@
    * remove the second.
    */
 
+  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (before)
+  /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
+  /// CHECK-DAG:     <<Const1:i\d+>>    IntConstant 1
+  /// CHECK-DAG:     <<Result:z\d+>>    InvokeStaticOrDirect
+  /// CHECK-DAG:     <<NotResult:i\d+>> Xor [<<Result>>,<<Const1>>]
+  /// CHECK-DAG:                        Return [<<NotResult>>]
+
+  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after)
+  /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
+  /// CHECK-DAG:     <<Result:z\d+>>    InvokeStaticOrDirect
+  /// CHECK-DAG:     <<NotResult:z\d+>> BooleanNot [<<Result>>]
+  /// CHECK-DAG:                        Return [<<NotResult>>]
+
   /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_bce (before)
   /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
-  /// CHECK-DAG:     <<Const0:i\d+>>    IntConstant 0
-  /// CHECK-DAG:     <<Const1:i\d+>>    IntConstant 1
-  /// CHECK-DAG:     <<NotArg:i\d+>>    Select [<<Const1>>,<<Const0>>,<<Arg>>]
-  /// CHECK-DAG:     <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>]
+  /// CHECK-DAG:     <<NotArg:z\d+>>    BooleanNot [<<Arg>>]
+  /// CHECK-DAG:     <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
   /// CHECK-DAG:                        Return [<<NotNotArg>>]
 
   /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_bce (after)
   /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
+  /// CHECK-DAG:     <<NotArg:z\d+>>    BooleanNot [<<Arg>>]
+  /// CHECK-DAG:                        Return [<<Arg>>]
+
+  /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
+  /// CHECK-DAG:     <<Arg:z\d+>>       ParameterValue
   /// CHECK-DAG:                        Return [<<Arg>>]
 
   public static boolean NegateValue(boolean arg) {
diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc
index 8122c6d..7b1ab9c 100644
--- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc
+++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc
@@ -17,7 +17,7 @@
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "jni.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
@@ -70,7 +70,7 @@
 extern "C" JNIEXPORT jint JNICALL Java_Main_doNativeCallRef(JNIEnv*, jobject value) {
   ScopedObjectAccess soa(Thread::Current());
   std::unique_ptr<Context> context(Context::Create());
-  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value));
+  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object>(value).Decode());
   visitor.WalkStack();
   return visitor.found_method_index_;
 }
diff --git a/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali b/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali
new file mode 100644
index 0000000..765d0eb
--- /dev/null
+++ b/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali
@@ -0,0 +1,65 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LBooleanNotSmali;
+.super Ljava/lang/Object;
+
+#
+# Elementary test negating a boolean. Verifies that blocks are merged and
+# empty branches removed.
+#
+
+## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (before)
+## CHECK-DAG:     <<Param:z\d+>>    ParameterValue
+## CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
+## CHECK-DAG:                       If [<<Param>>]
+## CHECK-DAG:     <<Phi:i\d+>>      Phi [<<Const0>>,<<Const1>>]
+## CHECK-DAG:                       Return [<<Phi>>]
+
+## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (before)
+## CHECK:                           Goto
+## CHECK:                           Goto
+## CHECK:                           Goto
+## CHECK-NOT:                       Goto
+
+## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (after)
+## CHECK-DAG:     <<Param:z\d+>>    ParameterValue
+## CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
+## CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
+## CHECK-DAG:     <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>]
+## CHECK-DAG:                       Return [<<NotParam>>]
+
+## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (after)
+## CHECK-NOT:                       If
+## CHECK-NOT:                       Phi
+
+## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (after)
+## CHECK:                           Goto
+## CHECK-NOT:                       Goto
+
+.method public static BooleanNot(Z)Z
+  .registers 2
+
+  if-eqz v1, :true_start
+  const/4 v0, 0x0
+
+:return_start
+  return v0
+
+:true_start
+  const/4 v0, 0x1
+  goto :return_start
+
+.end method
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index f0fe1b1..9368488 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -32,42 +32,14 @@
     }
   }
 
-  /*
-   * Elementary test negating a boolean. Verifies that blocks are merged and
-   * empty branches removed.
-   */
-
-  /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
-  /// CHECK-DAG:     <<Param:z\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
-  /// CHECK-DAG:                       If [<<Param>>]
-  /// CHECK-DAG:     <<Phi:i\d+>>      Phi [<<Const0>>,<<Const1>>]
-  /// CHECK-DAG:                       Return [<<Phi>>]
-
-  /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
-  /// CHECK:                           Goto
-  /// CHECK:                           Goto
-  /// CHECK:                           Goto
-  /// CHECK-NOT:                       Goto
-
-  /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
-  /// CHECK-DAG:     <<Param:z\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Const0:i\d+>>   IntConstant 0
-  /// CHECK-DAG:     <<Const1:i\d+>>   IntConstant 1
-  /// CHECK-DAG:     <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>]
-  /// CHECK-DAG:                       Return [<<NotParam>>]
-
-  /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
-  /// CHECK-NOT:                       If
-  /// CHECK-NOT:                       Phi
-
-  /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
-  /// CHECK:                           Goto
-  /// CHECK-NOT:                       Goto
-
-  public static boolean BooleanNot(boolean x) {
-    return !x;
+  // Invoke a method written in smali that implements the boolean ! operator. This method
+  // uses the if/else pattern generated by dx (while Jack generates a different pattern).
+  // Since this method is in a smali-generated class, we invoke it through reflection.
+  public static boolean SmaliBooleanNot(boolean x) throws Exception {
+      Class<?> c = Class.forName("BooleanNotSmali");
+      java.lang.reflect.Method method = c.getMethod("BooleanNot", boolean.class);
+      Object retValue = method.invoke(null, new Object[] { Boolean.valueOf(x) });
+      return ((Boolean) retValue).booleanValue();
   }
 
   /*
@@ -357,9 +329,9 @@
     return x ? 42 : (write_field = 43);
   }
 
-  public static void main(String[] args) {
-    assertBoolEquals(false, BooleanNot(true));
-    assertBoolEquals(true, BooleanNot(false));
+  public static void main(String[] args) throws Exception {
+    assertBoolEquals(false, SmaliBooleanNot(true));
+    assertBoolEquals(true, SmaliBooleanNot(false));
     assertBoolEquals(true, GreaterThan(10, 5));
     assertBoolEquals(false, GreaterThan(10, 10));
     assertBoolEquals(false, GreaterThan(5, 10));
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index 3618b4f..d3a033b 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -18,7 +18,7 @@
 #include "art_method-inl.h"
 #include "jni.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index 1597c4a..3f2df29 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -17,7 +17,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "jni.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
@@ -29,7 +29,7 @@
                                                                     jclass,
                                                                     jclass cls) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+  mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
   size_t num_methods = dex_cache->NumResolvedMethods();
   ArtMethod** methods = dex_cache->GetResolvedMethods();
   CHECK_EQ(num_methods != 0u, methods != nullptr);
@@ -43,7 +43,7 @@
     array = env->NewLongArray(num_methods);
   }
   CHECK(array != nullptr);
-  mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray*>(array);
+  mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray>(array).Decode();
   for (size_t i = 0; i != num_methods; ++i) {
     ArtMethod* method = mirror::DexCache::GetElementPtrSize(methods, i, kRuntimePointerSize);
     pointer_array->SetElementPtrSize(i, method, kRuntimePointerSize);
@@ -54,11 +54,11 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods(
     JNIEnv*, jclass, jclass cls, jobject old_cache) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+  mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
   size_t num_methods = dex_cache->NumResolvedMethods();
-  ArtMethod** methods = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+  ArtMethod** methods = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetResolvedMethods();
   CHECK_EQ(num_methods != 0u, methods != nullptr);
-  mirror::PointerArray* old = soa.Decode<mirror::PointerArray*>(old_cache);
+  ObjPtr<mirror::PointerArray> old = soa.Decode<mirror::PointerArray>(old_cache);
   CHECK_EQ(methods != nullptr, old != nullptr);
   CHECK_EQ(num_methods, static_cast<size_t>(old->GetLength()));
   for (size_t i = 0; i != num_methods; ++i) {
diff --git a/test/543-env-long-ref/env_long_ref.cc b/test/543-env-long-ref/env_long_ref.cc
index 557def6..cd127ef 100644
--- a/test/543-env-long-ref/env_long_ref.cc
+++ b/test/543-env-long-ref/env_long_ref.cc
@@ -17,7 +17,7 @@
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "jni.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread.h"
 
@@ -43,7 +43,7 @@
       uint32_t value = 0;
       CHECK(GetVReg(m, 1, kReferenceVReg, &value));
       CHECK_EQ(reinterpret_cast<mirror::Object*>(value),
-               soa_.Decode<mirror::Object*>(expected_value_));
+               soa_.Decode<mirror::Object>(expected_value_).Decode());
     }
     return true;
   }
diff --git a/test/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build
deleted file mode 100644
index 92ddfc9..0000000
--- a/test/555-checker-regression-x86const/build
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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 can't use src-ex testing infrastructure because src and src-ex are compiled
-# with javac independetely and can't share code (without reflection).
-
-mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
-
-mkdir classes-ex
-mv classes/UnresolvedClass.class classes-ex
-
-if [ ${USE_JACK} = "true" ]; then
-  jar cf classes.jill.jar -C classes .
-  jar cf classes-ex.jill.jar -C classes-ex .
-
-  ${JACK} --import classes.jill.jar --output-dex .
-  zip $TEST_NAME.jar classes.dex
-  ${JACK} --import classes-ex.jill.jar --output-dex .
-  zip ${TEST_NAME}-ex.jar classes.dex
-else
-  if [ ${NEED_DEX} = "true" ]; then
-    ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
-    zip $TEST_NAME.jar classes.dex
-    ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
-    zip ${TEST_NAME}-ex.jar classes.dex
-  fi
-fi
diff --git a/test/555-checker-regression-x86const/expected.txt b/test/555-checker-regression-x86const/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/555-checker-regression-x86const/expected.txt
+++ /dev/null
diff --git a/test/555-checker-regression-x86const/info.txt b/test/555-checker-regression-x86const/info.txt
deleted file mode 100644
index c4037fa..0000000
--- a/test/555-checker-regression-x86const/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Check that X86 FP constant-area handling handles intrinsics with CurrentMethod
-on the call.
diff --git a/test/555-checker-regression-x86const/src/Main.java b/test/555-checker-regression-x86const/src/Main.java
deleted file mode 100644
index 914cfde..0000000
--- a/test/555-checker-regression-x86const/src/Main.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 UnresolvedClass {
-
-  /// CHECK-START: float Main.callAbs(float) register (before)
-  /// CHECK:       <<CurrentMethod:[ij]\d+>> CurrentMethod
-  /// CHECK:       <<ParamValue:f\d+>> ParameterValue
-  /// CHECK:       InvokeStaticOrDirect [<<ParamValue>>,<<CurrentMethod>>] method_name:java.lang.Math.abs
-  static public float callAbs(float f) {
-    // An intrinsic invoke in a method that has unresolved references will still
-    // have a CurrentMethod as an argument.  The X86 pc_relative_fixups_x86 pass
-    // must be able to handle Math.abs invokes that have a CurrentMethod, as both
-    // the CurrentMethod and the HX86LoadFromConstantTable (for the bitmask)
-    // expect to be in the 'SpecialInputIndex' input index.
-    return Math.abs(f);
-  }
-
-  static public void main(String[] args) {
-    expectEquals(callAbs(-6.5f), 6.5f);
-  }
-
-  public static void expectEquals(float expected, float result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-}
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index 811c280..5ccc648 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -70,20 +70,19 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (before)
   /// CHECK:       <<P1:z\d+>>          ParameterValue
   /// CHECK:       <<P2:z\d+>>          ParameterValue
-  /// CHECK-DAG:   <<Const0:i\d+>>      IntConstant 0
   /// CHECK-DAG:   <<Const1:i\d+>>      IntConstant 1
-  /// CHECK:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
-  /// CHECK:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
-  /// CHECK:       <<And:i\d+>>         And [<<Select2>>,<<Select1>>]
+  /// CHECK-DAG:   <<NotP1:i\d+>>       Xor [<<P1>>,<<Const1>>]
+  /// CHECK-DAG:   <<NotP2:i\d+>>       Xor [<<P2>>,<<Const1>>]
+  /// CHECK:       <<And:i\d+>>         And [<<NotP1>>,<<NotP2>>]
   /// CHECK:                            Return [<<And>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (after)
   /// CHECK:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK:       <<Cond2:z\d+>>       ParameterValue
-  /// CHECK:       <<Or:i\d+>>          Or [<<Cond2>>,<<Cond1>>]
+  /// CHECK:       <<Or:i\d+>>          Or [<<Cond1>>,<<Cond2>>]
   /// CHECK:       <<BooleanNot:z\d+>>  BooleanNot [<<Or>>]
   /// CHECK:                            Return [<<BooleanNot>>]
 
@@ -138,20 +137,19 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (before)
   /// CHECK:       <<P1:z\d+>>          ParameterValue
   /// CHECK:       <<P2:z\d+>>          ParameterValue
-  /// CHECK-DAG:   <<Const0:i\d+>>      IntConstant 0
   /// CHECK-DAG:   <<Const1:i\d+>>      IntConstant 1
-  /// CHECK:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
-  /// CHECK:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
-  /// CHECK:       <<Or:i\d+>>          Or [<<Select2>>,<<Select1>>]
+  /// CHECK:       <<NotP1:i\d+>>       Xor [<<P1>>,<<Const1>>]
+  /// CHECK:       <<NotP2:i\d+>>       Xor [<<P2>>,<<Const1>>]
+  /// CHECK:       <<Or:i\d+>>          Or [<<NotP1>>,<<NotP2>>]
   /// CHECK:                            Return [<<Or>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (after)
   /// CHECK:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK:       <<Cond2:z\d+>>       ParameterValue
-  /// CHECK:       <<And:i\d+>>         And [<<Cond2>>,<<Cond1>>]
+  /// CHECK:       <<And:i\d+>>         And [<<Cond1>>,<<Cond2>>]
   /// CHECK:       <<BooleanNot:z\d+>>  BooleanNot [<<And>>]
   /// CHECK:                            Return [<<BooleanNot>>]
 
@@ -246,20 +244,19 @@
    * same pass.
    */
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (before)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (before)
   /// CHECK:       <<P1:z\d+>>          ParameterValue
   /// CHECK:       <<P2:z\d+>>          ParameterValue
-  /// CHECK-DAG:   <<Const0:i\d+>>      IntConstant 0
   /// CHECK-DAG:   <<Const1:i\d+>>      IntConstant 1
-  /// CHECK:       <<Select1:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P1>>]
-  /// CHECK:       <<Select2:i\d+>>     Select [<<Const1>>,<<Const0>>,<<P2>>]
-  /// CHECK:       <<Xor:i\d+>>         Xor [<<Select2>>,<<Select1>>]
+  /// CHECK:       <<NotP1:i\d+>>       Xor [<<P1>>,<<Const1>>]
+  /// CHECK:       <<NotP2:i\d+>>       Xor [<<P2>>,<<Const1>>]
+  /// CHECK:       <<Xor:i\d+>>         Xor [<<NotP1>>,<<NotP2>>]
   /// CHECK:                            Return [<<Xor>>]
 
-  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
+  /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (after)
   /// CHECK:       <<Cond1:z\d+>>       ParameterValue
   /// CHECK:       <<Cond2:z\d+>>       ParameterValue
-  /// CHECK:       <<Xor:i\d+>>         Xor [<<Cond2>>,<<Cond1>>]
+  /// CHECK:       <<Xor:i\d+>>         Xor [<<Cond1>>,<<Cond2>>]
   /// CHECK:                            Return [<<Xor>>]
 
   /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
index 89293cc..00c1b02 100644
--- a/test/566-polymorphic-inlining/polymorphic_inline.cc
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -20,14 +20,14 @@
 #include "jit/jit_code_cache.h"
 #include "jit/profiling_info.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack_map.h"
 
 namespace art {
 
 static void do_checks(jclass cls, const char* method_name) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
   jit::Jit* jit = Runtime::Current()->GetJit();
   jit::JitCodeCache* code_cache = jit->GetCodeCache();
   ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, kRuntimePointerSize);
@@ -53,7 +53,7 @@
 
 static void allocate_profiling_info(jclass cls, const char* method_name) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
   ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, kRuntimePointerSize);
   ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true);
 }
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index adda3cc..50e8382 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -19,7 +19,7 @@
 #include "jit/jit_code_cache.h"
 #include "jit/profiling_info.h"
 #include "oat_quick_method_header.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "stack_map.h"
 
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index a265dce..bf3d812 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -24,7 +24,7 @@
 #include "mirror/class-inl.h"
 #include "oat_file_assistant.h"
 #include "oat_file_manager.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "thread.h"
 
@@ -74,7 +74,7 @@
   ScopedUtfChars filename_chars(env, filename);
   CHECK(filename_chars.c_str() != nullptr);
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* dex_file = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile();
+  const DexFile* dex_file = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetDexFile();
   return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
                                      dex_file,
                                      static_cast<uint16_t>(method_index));
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
index a5bbf5f..78cc3fd 100644
--- a/test/596-app-images/app_images.cc
+++ b/test/596-app-images/app_images.cc
@@ -26,7 +26,7 @@
 #include "jni.h"
 #include "mirror/class.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -48,13 +48,13 @@
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageContains(JNIEnv*, jclass, jclass c) {
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* klass_ptr = soa.Decode<mirror::Class*>(c);
+  ObjPtr<mirror::Class> klass_ptr = soa.Decode<mirror::Class>(c);
   for (auto* space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) {
     if (space->IsImageSpace()) {
       auto* image_space = space->AsImageSpace();
       const auto& image_header = image_space->GetImageHeader();
       if (image_header.IsAppImage()) {
-        if (image_space->HasAddress(klass_ptr)) {
+        if (image_space->HasAddress(klass_ptr.Decode())) {
           return JNI_TRUE;
         }
       }
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 3bb3725..b8324e5 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -70,4 +70,5 @@
 b/29778499 (1)
 b/29778499 (2)
 b/30458218
+b/31313170
 Done!
diff --git a/test/800-smali/smali/b_31313170.smali b/test/800-smali/smali/b_31313170.smali
new file mode 100644
index 0000000..327942a
--- /dev/null
+++ b/test/800-smali/smali/b_31313170.smali
@@ -0,0 +1,22 @@
+.class public LB31313170;
+.super Ljava/lang/Object;
+
+
+.method public constructor <init>()V
+.registers 1
+       invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+       return-void
+.end method
+
+.method public static run()I
+.registers 4
+       const/4 v0, 0
+       const/4 v1, 1
+       sget v2, LB31313170;->a:I
+       if-nez v2, :exit
+       move-object v1, v0
+       :exit
+       return v1
+.end method
+
+.field static public a:I
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 34f2580..8d39f09 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -181,6 +181,7 @@
         testCases.add(new TestCase("b/29778499 (2)", "B29778499_2", "run", null,
                 new IncompatibleClassChangeError(), null));
         testCases.add(new TestCase("b/30458218", "B30458218", "run", null, null, null));
+        testCases.add(new TestCase("b/31313170", "B31313170", "run", null, null, 0));
     }
 
     public void runTests() {
@@ -228,7 +229,7 @@
                                                             tc.testName);
                 } else if (tc.expectedReturn == null && retValue != null) {
                     errorReturn = new IllegalStateException("Expected a null result in test " +
-                                                            tc.testName);
+                                                            tc.testName + " got " + retValue);
                 } else if (tc.expectedReturn != null &&
                            (retValue == null || !tc.expectedReturn.equals(retValue))) {
                     errorReturn = new IllegalStateException("Expected return " +
diff --git a/test/555-checker-regression-x86const/run b/test/902-hello-transformation/build
old mode 100644
new mode 100755
similarity index 71%
rename from test/555-checker-regression-x86const/run
rename to test/902-hello-transformation/build
index 63fdb8c..898e2e5
--- a/test/555-checker-regression-x86const/run
+++ b/test/902-hello-transformation/build
@@ -1,12 +1,12 @@
 #!/bin/bash
 #
-# Copyright (C) 2015 The Android Open Source Project
+# Copyright 2016 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
-#     http://www.apache.org/licenses/LICENSE-2.0
+#      http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,5 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
+./default-build "$@" --experimental agents
diff --git a/test/902-hello-transformation/expected.txt b/test/902-hello-transformation/expected.txt
new file mode 100644
index 0000000..e86e814
--- /dev/null
+++ b/test/902-hello-transformation/expected.txt
@@ -0,0 +1,3 @@
+Hello
+modifying class 'Transform'
+Goodbye
diff --git a/test/902-hello-transformation/info.txt b/test/902-hello-transformation/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/902-hello-transformation/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run
new file mode 100755
index 0000000..204e4cc
--- /dev/null
+++ b/test/902-hello-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+  other_args=" -Xcompiler-option --debuggable "
+else
+  other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=902-hello-transformation,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.h b/test/902-hello-transformation/src/Main.java
similarity index 63%
copy from runtime/native/java_lang_reflect_AbstractMethod.h
copy to test/902-hello-transformation/src/Main.java
index 222e5a0..204b6e7 100644
--- a/runtime/native/java_lang_reflect_AbstractMethod.h
+++ b/test/902-hello-transformation/src/Main.java
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform());
+  }
 
-#include <jni.h>
+  public static void doTest(Transform t) {
+    t.sayHi();
+    doClassTransformation(Transform.class);
+    t.sayHi();
+  }
 
-namespace art {
-
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env);
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+  // Transforms the class
+  private static native void doClassTransformation(Class target);
+}
diff --git a/test/555-checker-regression-x86const/src/Unresolved.java b/test/902-hello-transformation/src/Transform.java
similarity index 88%
copy from test/555-checker-regression-x86const/src/Unresolved.java
copy to test/902-hello-transformation/src/Transform.java
index e98bdbf..dc0a0c4 100644
--- a/test/555-checker-regression-x86const/src/Unresolved.java
+++ b/test/902-hello-transformation/src/Transform.java
@@ -14,5 +14,8 @@
  * limitations under the License.
  */
 
-class UnresolvedClass {
+class Transform {
+  public void sayHi() {
+    System.out.println("Hello");
+  }
 }
diff --git a/test/902-hello-transformation/transform.cc b/test/902-hello-transformation/transform.cc
new file mode 100644
index 0000000..e0d623e
--- /dev/null
+++ b/test/902-hello-transformation/transform.cc
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "utils.h"
+
+namespace art {
+namespace Test902HelloTransformation {
+
+static bool RuntimeIsJvm = false;
+
+jvmtiEnv* jvmti_env;
+bool IsJVM() {
+  return RuntimeIsJvm;
+}
+
+// base64 encoded class/dex file for
+//
+// class Transform {
+//   public void sayHi() {
+//     System.out.println("Goodbye");
+//   }
+// }
+const char* class_file_base64 =
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB"
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA"
+    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq"
+    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph"
+    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG"
+    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB"
+    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=";
+
+const char* dex_file_base64 =
+    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO"
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB"
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA"
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA"
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA"
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA"
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50"
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh"
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291"
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA"
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA"
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA"
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=";
+
+static void JNICALL transformationHook(jvmtiEnv *jvmtienv,
+                                       JNIEnv* jni_env                 ATTRIBUTE_UNUSED,
+                                       jclass class_being_redefined    ATTRIBUTE_UNUSED,
+                                       jobject loader                  ATTRIBUTE_UNUSED,
+                                       const char* name,
+                                       jobject protection_domain       ATTRIBUTE_UNUSED,
+                                       jint class_data_len             ATTRIBUTE_UNUSED,
+                                       const unsigned char* class_data ATTRIBUTE_UNUSED,
+                                       jint* new_class_data_len,
+                                       unsigned char** new_class_data) {
+  if (strcmp("Transform", name)) {
+    return;
+  }
+  printf("modifying class '%s'\n", name);
+  bool is_jvm = IsJVM();
+  size_t decode_len = 0;
+  unsigned char* new_data;
+  std::unique_ptr<uint8_t[]> file_data(
+      DecodeBase64((is_jvm) ? class_file_base64 : dex_file_base64, &decode_len));
+  jvmtiError ret = JVMTI_ERROR_NONE;
+  if ((ret = jvmtienv->Allocate(static_cast<jlong>(decode_len), &new_data)) != JVMTI_ERROR_NONE) {
+    printf("Unable to allocate buffer!\n");
+    return;
+  }
+  memcpy(new_data, file_data.get(), decode_len);
+  *new_class_data_len = static_cast<jint>(decode_len);
+  *new_class_data = new_data;
+  return;
+}
+
+using RetransformWithHookFunction = jvmtiError (*)(jvmtiEnv*, jclass, jvmtiEventClassFileLoadHook);
+static void DoClassTransformation(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jclass target) {
+  if (IsJVM()) {
+    UNUSED(jnienv);
+    jvmtienv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr);
+    jvmtiError ret = jvmtienv->RetransformClasses(1, &target);
+    if (ret != JVMTI_ERROR_NONE) {
+      char* err;
+      jvmtienv->GetErrorName(ret, &err);
+      printf("Error transforming: %s\n", err);
+    }
+  } else {
+    RetransformWithHookFunction f =
+        reinterpret_cast<RetransformWithHookFunction>(jvmtienv->functions->reserved1);
+    if (f(jvmtienv, target, transformationHook) != JVMTI_ERROR_NONE) {
+      printf("Failed to tranform class!");
+      return;
+    }
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doClassTransformation(JNIEnv* env,
+                                                                  jclass,
+                                                                  jclass target) {
+  JavaVM* vm;
+  if (env->GetJavaVM(&vm)) {
+    printf("Unable to get javaVM!\n");
+    return;
+  }
+  DoClassTransformation(jvmti_env, env, target);
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options,
+            void* reserved ATTRIBUTE_UNUSED) {
+  jvmtiCapabilities caps;
+  RuntimeIsJvm = (strcmp("jvm", options) == 0);
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  if (IsJVM()) {
+    jvmti_env->GetPotentialCapabilities(&caps);
+    jvmti_env->AddCapabilities(&caps);
+    jvmtiEventCallbacks cbs;
+    memset(&cbs, 0, sizeof(cbs));
+    cbs.ClassFileLoadHook = transformationHook;
+    jvmti_env->SetEventCallbacks(&cbs, sizeof(jvmtiEventCallbacks));
+  }
+  return 0;
+}
+
+}  // namespace Test902HelloTransformation
+}  // namespace art
+
diff --git a/runtime/native/java_lang_reflect_AbstractMethod.h b/test/902-hello-transformation/transform.h
similarity index 68%
copy from runtime/native/java_lang_reflect_AbstractMethod.h
copy to test/902-hello-transformation/transform.h
index 222e5a0..661058d 100644
--- a/runtime/native/java_lang_reflect_AbstractMethod.h
+++ b/test/902-hello-transformation/transform.h
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#ifndef ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
+#define ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
 
 #include <jni.h>
 
 namespace art {
+namespace Test902HelloTransformation {
 
-void register_java_lang_reflect_AbstractMethod(JNIEnv* env);
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
 
+}  // namespace Test902HelloTransformation
 }  // namespace art
 
-#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_REFLECT_ABSTRACTMETHOD_H_
+#endif  // ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
diff --git a/test/961-default-iface-resolut-generated/build b/test/961-default-iface-resolution-gen/build
similarity index 100%
rename from test/961-default-iface-resolut-generated/build
rename to test/961-default-iface-resolution-gen/build
diff --git a/test/961-default-iface-resolut-generated/expected.txt b/test/961-default-iface-resolution-gen/expected.txt
similarity index 100%
rename from test/961-default-iface-resolut-generated/expected.txt
rename to test/961-default-iface-resolution-gen/expected.txt
diff --git a/test/961-default-iface-resolut-generated/info.txt b/test/961-default-iface-resolution-gen/info.txt
similarity index 100%
rename from test/961-default-iface-resolut-generated/info.txt
rename to test/961-default-iface-resolution-gen/info.txt
diff --git a/test/961-default-iface-resolut-generated/util-src/generate_java.py b/test/961-default-iface-resolution-gen/util-src/generate_java.py
similarity index 100%
rename from test/961-default-iface-resolut-generated/util-src/generate_java.py
rename to test/961-default-iface-resolution-gen/util-src/generate_java.py
diff --git a/test/964-default-iface-init-generated/build b/test/964-default-iface-init-gen/build
similarity index 100%
rename from test/964-default-iface-init-generated/build
rename to test/964-default-iface-init-gen/build
diff --git a/test/964-default-iface-init-generated/expected.txt b/test/964-default-iface-init-gen/expected.txt
similarity index 100%
rename from test/964-default-iface-init-generated/expected.txt
rename to test/964-default-iface-init-gen/expected.txt
diff --git a/test/964-default-iface-init-generated/info.txt b/test/964-default-iface-init-gen/info.txt
similarity index 100%
rename from test/964-default-iface-init-generated/info.txt
rename to test/964-default-iface-init-gen/info.txt
diff --git a/test/964-default-iface-init-generated/src/Displayer.java b/test/964-default-iface-init-gen/src/Displayer.java
similarity index 100%
rename from test/964-default-iface-init-generated/src/Displayer.java
rename to test/964-default-iface-init-gen/src/Displayer.java
diff --git a/test/964-default-iface-init-generated/util-src/generate_java.py b/test/964-default-iface-init-gen/util-src/generate_java.py
similarity index 100%
rename from test/964-default-iface-init-generated/util-src/generate_java.py
rename to test/964-default-iface-init-gen/util-src/generate_java.py
diff --git a/test/968-default-part-compile-generated/build b/test/968-default-partial-compile-gen/build
similarity index 100%
rename from test/968-default-part-compile-generated/build
rename to test/968-default-partial-compile-gen/build
diff --git a/test/968-default-part-compile-generated/expected.txt b/test/968-default-partial-compile-gen/expected.txt
similarity index 100%
rename from test/968-default-part-compile-generated/expected.txt
rename to test/968-default-partial-compile-gen/expected.txt
diff --git a/test/968-default-part-compile-generated/info.txt b/test/968-default-partial-compile-gen/info.txt
similarity index 100%
rename from test/968-default-part-compile-generated/info.txt
rename to test/968-default-partial-compile-gen/info.txt
diff --git a/test/968-default-part-compile-generated/util-src/generate_java.py b/test/968-default-partial-compile-gen/util-src/generate_java.py
similarity index 100%
rename from test/968-default-part-compile-generated/util-src/generate_java.py
rename to test/968-default-partial-compile-gen/util-src/generate_java.py
diff --git a/test/968-default-part-compile-generated/util-src/generate_smali.py b/test/968-default-partial-compile-gen/util-src/generate_smali.py
similarity index 100%
rename from test/968-default-part-compile-generated/util-src/generate_smali.py
rename to test/968-default-partial-compile-gen/util-src/generate_smali.py
diff --git a/test/970-iface-superresolution-generated/build b/test/970-iface-super-resolution-gen/build
similarity index 100%
rename from test/970-iface-superresolution-generated/build
rename to test/970-iface-super-resolution-gen/build
diff --git a/test/970-iface-superresolution-generated/expected.txt b/test/970-iface-super-resolution-gen/expected.txt
similarity index 100%
rename from test/970-iface-superresolution-generated/expected.txt
rename to test/970-iface-super-resolution-gen/expected.txt
diff --git a/test/970-iface-superresolution-generated/info.txt b/test/970-iface-super-resolution-gen/info.txt
similarity index 100%
rename from test/970-iface-superresolution-generated/info.txt
rename to test/970-iface-super-resolution-gen/info.txt
diff --git a/test/970-iface-superresolution-generated/util-src/generate_java.py b/test/970-iface-super-resolution-gen/util-src/generate_java.py
similarity index 100%
rename from test/970-iface-superresolution-generated/util-src/generate_java.py
rename to test/970-iface-super-resolution-gen/util-src/generate_java.py
diff --git a/test/970-iface-superresolution-generated/util-src/generate_smali.py b/test/970-iface-super-resolution-gen/util-src/generate_smali.py
similarity index 100%
rename from test/970-iface-superresolution-generated/util-src/generate_smali.py
rename to test/970-iface-super-resolution-gen/util-src/generate_smali.py
diff --git a/test/Android.bp b/test/Android.bp
index ff408f4..628f377 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -40,6 +40,9 @@
             enabled: false,
         },
     },
+    cflags: [
+        "-Wno-frame-larger-than=",
+    ],
 }
 
 art_cc_defaults {
@@ -60,6 +63,7 @@
         "libvixld-arm64",
         "libart-gtest",
 
+        "libbase",
         "libicuuc",
         "libicui18n",
         "libnativehelper",
@@ -106,7 +110,7 @@
                 "-Wl,-u,Java_MyClassNatives_sbar",
             ],
             shared_libs: [
-                "libcutils",
+                "liblog",
                 "libdl",
                 "libz",
             ],
@@ -201,6 +205,7 @@
     ],
     shared_libs: [
         "libbacktrace",
+        "libbase",
         "libnativehelper",
     ],
     target: {
@@ -239,9 +244,11 @@
     srcs: [
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
+        "902-hello-transformation/transform.cc",
     ],
     shared_libs: [
         "libart",
+        "libbase",
         "libopenjdkjvmti",
     ],
 }
@@ -255,6 +262,7 @@
     srcs: [
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
+        "902-hello-transformation/transform.cc",
     ],
     shared_libs: [
         "libartd",
@@ -301,6 +309,7 @@
     ],
     shared_libs: [
         "libbacktrace",
+        "libbase",
         "libnativehelper",
     ],
     target: {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 559e963..211a69f 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -263,9 +263,12 @@
 # 147-stripped-dex-fallback isn't supported on device because --strip-dex
 # requires the zip command.
 # 569-checker-pattern-replacement tests behaviour present only on host.
+# 902-hello-transformation isn't supported in current form due to linker
+# restrictions. See b/31681198
 TEST_ART_BROKEN_TARGET_TESTS := \
   147-stripped-dex-fallback \
-  569-checker-pattern-replacement
+  569-checker-pattern-replacement \
+  902-hello-transformation
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -278,11 +281,11 @@
 # Tests that require python3.
 TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS := \
   960-default-smali \
-  961-default-iface-resolut-generated \
-  964-default-iface-init-generated \
-  968-default-part-compile-generated \
+  961-default-iface-resolution-gen \
+  964-default-iface-init-gen \
+  968-default-partial-compile-gen \
   969-iface-super \
-  970-iface-superresolution-generated \
+  970-iface-super-resolution-gen \
   971-iface-super
 
 # Check if we have python3 to run our tests.
@@ -346,9 +349,7 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
 
 # Temporarily disable some broken tests when forcing access checks in interpreter b/22414682
-# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \
-  004-JniTest \
   137-cfi
 
 ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
@@ -362,13 +363,13 @@
 # Tests that are broken with GC stress.
 # * 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we
 #   hope the second process got into the expected state. The slowness of gcstress makes this bad.
-# * 961-default-iface-resolut-generated and 964-default-iface-init-generated are very long tests
-#   that often will take more than the timeout to run when gcstress is enabled. This is because
-#   gcstress slows down allocations significantly which these tests do a lot.
+# * 961-default-iface-resolution-gen and 964-default-iface-init-genare very long tests that often
+#   will take more than the timeout to run when gcstress is enabled. This is because gcstress
+#   slows down allocations significantly which these tests do a lot.
 TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
   137-cfi \
-  961-default-iface-resolut-generated \
-  964-default-iface-init-generated
+  961-default-iface-resolution-gen \
+  964-default-iface-init-gen
 
 ifneq (,$(filter gcstress,$(GC_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -407,11 +408,9 @@
 # All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
 # Therefore we shouldn't run them in situations where we actually don't have these since they
 # explicitly test for them. These all also assume we have an image.
-# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 # 147-stripped-dex-fallback is disabled because it requires --prebuild.
 # 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
 TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
-  004-JniTest \
   116-nodex2oat \
   117-nopatchoat \
   118-noimage-dex2oat \
@@ -485,9 +484,7 @@
 # Known broken tests for the JIT.
 # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
 # also uses Generic JNI instead of the JNI compiler.
-# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
-  004-JniTest \
   137-cfi
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
diff --git a/test/555-checker-regression-x86const/src/Unresolved.java b/test/ImageLayoutA/ImageLayoutA.java
similarity index 89%
rename from test/555-checker-regression-x86const/src/Unresolved.java
rename to test/ImageLayoutA/ImageLayoutA.java
index e98bdbf..0784ec2 100644
--- a/test/555-checker-regression-x86const/src/Unresolved.java
+++ b/test/ImageLayoutA/ImageLayoutA.java
@@ -14,5 +14,8 @@
  * limitations under the License.
  */
 
-class UnresolvedClass {
+import java.util.HashMap;
+
+class MyClass {
+    static int i = 123;
 }
diff --git a/test/555-checker-regression-x86const/src/Unresolved.java b/test/ImageLayoutB/ImageLayoutB.java
similarity index 70%
copy from test/555-checker-regression-x86const/src/Unresolved.java
copy to test/ImageLayoutB/ImageLayoutB.java
index e98bdbf..a21c5e2 100644
--- a/test/555-checker-regression-x86const/src/Unresolved.java
+++ b/test/ImageLayoutB/ImageLayoutB.java
@@ -14,5 +14,12 @@
  * limitations under the License.
  */
 
-class UnresolvedClass {
+import java.util.HashMap;
+
+class MyClass {
+    public static String string = "ASDF_UNIQUE_STRING";
+    public static HashMap<String, String> map = new HashMap<String, String>();
+    static {
+        map.put("KEY_FOR_HASH_MAP", "VALUE_FOR_HASH_MAP");
+    }
 }
diff --git a/test/555-checker-regression-x86const/src/Unresolved.java b/test/MethodTypes/MethodTypes.java
similarity index 83%
copy from test/555-checker-regression-x86const/src/Unresolved.java
copy to test/MethodTypes/MethodTypes.java
index e98bdbf..f6f8e08 100644
--- a/test/555-checker-regression-x86const/src/Unresolved.java
+++ b/test/MethodTypes/MethodTypes.java
@@ -14,5 +14,7 @@
  * limitations under the License.
  */
 
-class UnresolvedClass {
+public interface MethodTypes {
+    public String method1(String a);
+    public String method2(String a, String b);
 }
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index fd1ba02..4248148 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -24,7 +24,7 @@
 #include "mirror/class-inl.h"
 #include "oat_quick_method_header.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "ScopedUtfChars.h"
 #include "thread-inl.h"
 
@@ -35,7 +35,7 @@
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOatFile(JNIEnv* env, jclass cls) {
   ScopedObjectAccess soa(env);
 
-  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
   const DexFile& dex_file = klass->GetDexFile();
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   return (oat_dex_file != nullptr) ? JNI_TRUE : JNI_FALSE;
@@ -75,7 +75,7 @@
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* env, jclass cls) {
   ScopedObjectAccess soa(env);
 
-  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
   const DexFile& dex_file = klass->GetDexFile();
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   if (oat_dex_file == nullptr) {
@@ -134,7 +134,7 @@
 
     ScopedUtfChars chars(env, method_name);
     CHECK(chars.c_str() != nullptr);
-    method = soa.Decode<mirror::Class*>(cls)->FindDeclaredDirectMethodByName(
+    method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
         chars.c_str(), kRuntimePointerSize);
   }
 
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 85ea1c8..d2aacf0 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -21,7 +21,7 @@
 #include "mirror/class-inl.h"
 #include "nth_caller_visitor.h"
 #include "runtime.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread-inl.h"
 
@@ -62,7 +62,7 @@
 static jboolean IsManaged(JNIEnv* env, jclass cls, size_t level) {
   ScopedObjectAccess soa(env);
 
-  mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
   const DexFile& dex_file = klass->GetDexFile();
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   if (oat_dex_file == nullptr) {
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index a445f4d..d8f42a2 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -310,8 +310,9 @@
 fi
 
 if [ "$USE_JVM" = "y" ]; then
+  export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
   # Xmx is necessary since we don't pass down the ART flags to JVM.
-  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@"
+  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@ ${ARGS}"
   if [ "$DEV_MODE" = "y" ]; then
     echo $cmdline
   fi
@@ -403,6 +404,24 @@
   fi
 fi
 
+# Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
+# when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
+# full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
+# remaining '/' into '@'.
+if [ "$HOST" = "y" ]; then
+  max_filename_size=$(getconf NAME_MAX $DEX_LOCATION)
+else
+  # There is no getconf on device, fallback to standard value. See NAME_MAX in kernel <linux/limits.h>
+  max_filename_size=255
+fi
+# Compute VDEX_NAME.
+DEX_LOCATION_STRIPPED="${DEX_LOCATION#/}"
+VDEX_NAME="${DEX_LOCATION_STRIPPED//\//@}@$TEST_NAME.jar@classes.vdex"
+if [ ${#VDEX_NAME} -gt $max_filename_size ]; then
+    echo  "Dex location path too long."
+    exit 1
+fi
+
 dex2oat_cmdline="true"
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
diff --git a/test/run-test b/test/run-test
index ae53f9e..7a4afaf 100755
--- a/test/run-test
+++ b/test/run-test
@@ -476,7 +476,7 @@
         run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
     else
         guess_target_arch_name
-        run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}:/system/lib${suffix64}"
+        run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}:${android_root}/lib${suffix64}"
         run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
     fi
     if [ "$relocate" = "yes" ]; then
@@ -743,9 +743,7 @@
   fi
 fi
 
-if [ "$runtime" != "jvm" ]; then
   run_args="${run_args} --testlib ${testlib}"
-fi
 
 # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
 build_file_size_limit=2048
@@ -939,6 +937,7 @@
         --raw-cmd="$raw_cmd" \
         --check-script="$cwd/check" \
         --expected-output="$cwd/expected.txt" \
+        --logfile="$cwd/bisection_log.txt" \
         --timeout=300
     fi
 fi
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index ed280e4..53bb153 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 
 #include "901-hello-ti-agent/basics.h"
+#include "902-hello-transformation/transform.h"
 
 namespace art {
 
@@ -39,6 +40,7 @@
 // A list of all the agents we have for testing.
 AgentLib agents[] = {
   { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
+  { "902-hello-transformation", Test902HelloTransformation::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 4003ee0..ebf087d 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -73,7 +73,7 @@
   $(ART_HOST_EXECUTABLES) \
   $(ART_HOST_SHARED_LIBRARY_DEPENDENCIES) \
   $(HOST_OUT_EXECUTABLES)/art \
-  $(HOST_CORE_IMG_OUT_BASE)-optimizing-pic$(CORE_IMG_SUFFIX)
+  $(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX)
 
 $(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art
 $(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR)
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index ecf9e53..8604ff0 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -9,7 +9,6 @@
        Serve pages on the given port. Defaults to 7100.
 
 TODO:
- * Show GC Root paths.
  * Have a way to diff two heap dumps.
 
  * Add more tips to the help page.
@@ -76,6 +75,7 @@
 
 Release History:
  0.8 Pending
+   Show sample path from GC root with field names in place of dominator path.
 
  0.7 Aug 16, 2016
    Launch ahat server before processing the heap dump.
diff --git a/tools/ahat/src/HeapTable.java b/tools/ahat/src/HeapTable.java
index ed11d17..5b84048 100644
--- a/tools/ahat/src/HeapTable.java
+++ b/tools/ahat/src/HeapTable.java
@@ -84,10 +84,10 @@
       for (Heap heap : heaps) {
         long size = config.getSize(elem, heap);
         total += size;
-        vals.add(DocString.format("%,14d", size));
+        vals.add(size == 0 ? DocString.text("") : DocString.format("%,14d", size));
       }
       if (showTotal) {
-        vals.add(DocString.format("%,14d", total));
+        vals.add(total == 0 ? DocString.text("") : DocString.format("%,14d", total));
       }
 
       for (ValueConfig<T> value : values) {
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 8769d11..94934a2 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -19,11 +19,17 @@
 import com.android.tools.perflib.heap.ArrayInstance;
 import com.android.tools.perflib.heap.ClassInstance;
 import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Field;
 import com.android.tools.perflib.heap.Heap;
 import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.RootObj;
 import com.android.tools.perflib.heap.Type;
 
 import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Utilities for extracting information from hprof instances.
@@ -179,7 +185,7 @@
    * Read a reference field of an instance.
    * Returns null if the field value is null, or if the field couldn't be read.
    */
-  private static Instance getRefField(Instance inst, String fieldName) {
+  public static Instance getRefField(Instance inst, String fieldName) {
     Object value = getField(inst, fieldName);
     if (!(value instanceof Instance)) {
       return null;
@@ -357,4 +363,90 @@
     }
     return new NativeAllocation(size, inst.getHeap(), pointer, referent);
   }
+
+  public static class PathElement {
+    public final Instance instance;
+    public final String field;
+    public boolean isDominator;
+
+    public PathElement(Instance instance, String field) {
+      this.instance = instance;
+      this.field = field;
+      this.isDominator = false;
+    }
+  }
+
+  /**
+   * Returns a sample path from a GC root to this instance.
+   * The given instance is included as the last element of the path with an
+   * empty field description.
+   */
+  public static List<PathElement> getPathFromGcRoot(Instance inst) {
+    List<PathElement> path = new ArrayList<PathElement>();
+
+    Instance dom = inst;
+    for (PathElement elem = new PathElement(inst, ""); elem != null;
+        elem = getNextPathElementToGcRoot(elem.instance)) {
+      if (elem.instance == dom) {
+        elem.isDominator = true;
+        dom = dom.getImmediateDominator();
+      }
+      path.add(elem);
+    }
+    Collections.reverse(path);
+    return path;
+  }
+
+  /**
+   * Returns the next instance to GC root from this object and a string
+   * description of which field of that object refers to the given instance.
+   * Returns null if the given instance has no next instance to the gc root.
+   */
+  private static PathElement getNextPathElementToGcRoot(Instance inst) {
+    Instance parent = inst.getNextInstanceToGcRoot();
+    if (parent == null || parent instanceof RootObj) {
+      return null;
+    }
+
+    // Search the parent for the reference to the child.
+    // TODO: This seems terribly inefficient. Can we use data structures to
+    // help us here?
+    String description = ".???";
+    if (parent instanceof ArrayInstance) {
+      ArrayInstance array = (ArrayInstance)parent;
+      Object[] values = array.getValues();
+      for (int i = 0; i < values.length; i++) {
+        if (values[i] instanceof Instance) {
+          Instance ref = (Instance)values[i];
+          if (ref.getId() == inst.getId()) {
+            description = String.format("[%d]", i);
+            break;
+          }
+        }
+      }
+    } else if (parent instanceof ClassObj) {
+      ClassObj cls = (ClassObj)parent;
+      for (Map.Entry<Field, Object> entries : cls.getStaticFieldValues().entrySet()) {
+        if (entries.getValue() instanceof Instance) {
+          Instance ref = (Instance)entries.getValue();
+          if (ref.getId() == inst.getId()) {
+            description = "." + entries.getKey().getName();
+            break;
+          }
+        }
+      }
+    } else if (parent instanceof ClassInstance) {
+      ClassInstance obj = (ClassInstance)parent;
+      for (ClassInstance.FieldValue fields : obj.getValues()) {
+        if (fields.getValue() instanceof Instance) {
+          Instance ref = (Instance)fields.getValue();
+          if (ref.getId() == inst.getId()) {
+            description = "." + fields.getField().getName();
+            break;
+          }
+        }
+      }
+    }
+    return new PathElement(parent, description);
+  }
 }
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 4df1be5..78aac17 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -22,7 +22,6 @@
 import com.android.tools.perflib.heap.Field;
 import com.android.tools.perflib.heap.Heap;
 import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
 import com.android.tools.perflib.heap.RootType;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -32,6 +31,8 @@
 import java.util.List;
 import java.util.Map;
 
+import static com.android.ahat.InstanceUtils.PathElement;
+
 class ObjectHandler implements AhatHandler {
 
   private static final String ARRAY_ELEMENTS_ID = "elements";
@@ -62,7 +63,7 @@
     doc.big(Value.render(mSnapshot, inst));
 
     printAllocationSite(doc, query, inst);
-    printDominatorPath(doc, query, inst);
+    printGcRootPath(doc, query, inst);
 
     doc.section("Object Info");
     ClassObj cls = inst.getClassObj();
@@ -202,43 +203,43 @@
     }
   }
 
-  private void printDominatorPath(Doc doc, Query query, Instance inst) {
-    doc.section("Dominator Path from Root");
-    List<Instance> path = new ArrayList<Instance>();
-    for (Instance parent = inst;
-        parent != null && !(parent instanceof RootObj);
-        parent = parent.getImmediateDominator()) {
-      path.add(parent);
-    }
+  private void printGcRootPath(Doc doc, Query query, Instance inst) {
+    doc.section("Sample Path from GC Root");
+    List<PathElement> path = InstanceUtils.getPathFromGcRoot(inst);
 
     // Add 'null' as a marker for the root.
-    path.add(null);
-    Collections.reverse(path);
+    path.add(0, null);
 
-    HeapTable.TableConfig<Instance> table = new HeapTable.TableConfig<Instance>() {
+    HeapTable.TableConfig<PathElement> table = new HeapTable.TableConfig<PathElement>() {
       public String getHeapsDescription() {
-        return "Bytes Retained by Heap";
+        return "Bytes Retained by Heap (Dominators Only)";
       }
 
-      public long getSize(Instance element, Heap heap) {
+      public long getSize(PathElement element, Heap heap) {
         if (element == null) {
           return mSnapshot.getHeapSize(heap);
         }
-        int index = mSnapshot.getHeapIndex(heap);
-        return element.getRetainedSize(index);
+        if (element.isDominator) {
+          int index = mSnapshot.getHeapIndex(heap);
+          return element.instance.getRetainedSize(index);
+        }
+        return 0;
       }
 
-      public List<HeapTable.ValueConfig<Instance>> getValueConfigs() {
-        HeapTable.ValueConfig<Instance> value = new HeapTable.ValueConfig<Instance>() {
+      public List<HeapTable.ValueConfig<PathElement>> getValueConfigs() {
+        HeapTable.ValueConfig<PathElement> value = new HeapTable.ValueConfig<PathElement>() {
           public String getDescription() {
-            return "Object";
+            return "Path Element";
           }
 
-          public DocString render(Instance element) {
+          public DocString render(PathElement element) {
             if (element == null) {
               return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
             } else {
-              return DocString.text("→ ").append(Value.render(mSnapshot, element));
+              DocString label = DocString.text(" → ");
+              label.append(Value.render(mSnapshot, element.instance));
+              label.append(element.field);
+              return label;
             }
           }
         };
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 3936f29..e08df67 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -29,6 +29,16 @@
   // collected before we take the heap dump.
   public static DumpedStuff stuff;
 
+  public static class ObjectTree {
+    public ObjectTree left;
+    public ObjectTree right;
+
+    public ObjectTree(ObjectTree left, ObjectTree right) {
+      this.left = left;
+      this.right = right;
+    }
+  }
+
   // We will take a heap dump that includes a single instance of this
   // DumpedStuff class. Objects stored as fields in this class can be easily
   // found in the hprof dump by searching for the instance of the DumpedStuff
@@ -42,6 +52,11 @@
     public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue);
     public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue);
     public byte[] bigArray;
+    public ObjectTree[] gcPathArray = new ObjectTree[]{null, null,
+      new ObjectTree(
+          new ObjectTree(null, new ObjectTree(null, null)),
+          new ObjectTree(null, null)),
+      null};
 
     DumpedStuff() {
       int N = 1000000;
@@ -53,6 +68,8 @@
       NativeAllocationRegistry registry = new NativeAllocationRegistry(
           Main.class.getClassLoader(), 0x12345, 42);
       registry.registerNativeAllocation(anObject, 0xABCDABCD);
+
+      gcPathArray[2].right.left = gcPathArray[2].left.right;
     }
   }
 
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
index 59b1c90..ec77e70 100644
--- a/tools/ahat/test/InstanceUtilsTest.java
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -16,11 +16,16 @@
 
 package com.android.ahat;
 
+import com.android.tools.perflib.heap.ArrayInstance;
+import com.android.tools.perflib.heap.ClassObj;
 import com.android.tools.perflib.heap.Instance;
 import java.io.IOException;
+import java.util.List;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import org.junit.Test;
 
 public class InstanceUtilsTest {
@@ -123,4 +128,55 @@
     assertEquals(referent, InstanceUtils.getReferent(wref));
     assertNull(InstanceUtils.getReferent(referent));
   }
+
+  @Test
+  public void gcRootPath() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+
+    ClassObj main = dump.getAhatSnapshot().findClass("Main");
+    ArrayInstance gcPathArray = (ArrayInstance)dump.getDumpedThing("gcPathArray");
+    Object[] values = gcPathArray.getValues();
+    Instance base = (Instance)values[2];
+    Instance left = InstanceUtils.getRefField(base, "left");
+    Instance right = InstanceUtils.getRefField(base, "right");
+    Instance target = InstanceUtils.getRefField(left, "right");
+
+    List<InstanceUtils.PathElement> path = InstanceUtils.getPathFromGcRoot(target);
+    assertEquals(6, path.size());
+
+    assertEquals(main, path.get(0).instance);
+    assertEquals(".stuff", path.get(0).field);
+    assertTrue(path.get(0).isDominator);
+
+    assertEquals(".gcPathArray", path.get(1).field);
+    assertTrue(path.get(1).isDominator);
+
+    assertEquals(gcPathArray, path.get(2).instance);
+    assertEquals("[2]", path.get(2).field);
+    assertTrue(path.get(2).isDominator);
+
+    assertEquals(base, path.get(3).instance);
+    assertTrue(path.get(3).isDominator);
+
+    // There are two possible paths. Either it can go through the 'left' node,
+    // or the 'right' node.
+    if (path.get(3).field.equals(".left")) {
+      assertEquals(".left", path.get(3).field);
+
+      assertEquals(left, path.get(4).instance);
+      assertEquals(".right", path.get(4).field);
+      assertFalse(path.get(4).isDominator);
+
+    } else {
+      assertEquals(".right", path.get(3).field);
+
+      assertEquals(right, path.get(4).instance);
+      assertEquals(".left", path.get(4).field);
+      assertFalse(path.get(4).isDominator);
+    }
+
+    assertEquals(target, path.get(5).instance);
+    assertEquals("", path.get(5).field);
+    assertTrue(path.get(5).isDominator);
+  }
 }
diff --git a/tools/bisection_search/README.md b/tools/bisection_search/README.md
index 64ccb20..d641102 100644
--- a/tools/bisection_search/README.md
+++ b/tools/bisection_search/README.md
@@ -21,12 +21,14 @@
 
         ./bisection_search.py -cp classes.dex --expected-output out_int --class Test
 
-2. Raw-cmd invocation, dalvikvm command is accepted as an argument. The command
-   has to start with an executable.
+2. Raw-cmd invocation, dalvikvm command is accepted as an argument.
 
    Extra dalvikvm arguments will be placed on second position in the command
    by default. {ARGS} tag can be used to specify a custom position.
 
+   If used in device mode, the command has to exec a dalvikvm instance. Bisection
+   will fail if pid of the process started by raw-cmd is different than pid of runtime.
+
         ./bisection_search.py --raw-cmd='run.sh -cp classes.dex Test' --expected-retcode SUCCESS
         ./bisection_search.py --raw-cmd='/bin/sh art {ARGS} -cp classes.dex Test' --expected-retcode SUCCESS
 
diff --git a/tools/bisection_search/bisection_search.py b/tools/bisection_search/bisection_search.py
index 0d36aa4..27bd599 100755
--- a/tools/bisection_search/bisection_search.py
+++ b/tools/bisection_search/bisection_search.py
@@ -24,17 +24,23 @@
 
 import abc
 import argparse
+import os
 import re
 import shlex
-from subprocess import call
 import sys
+
+from subprocess import call
 from tempfile import NamedTemporaryFile
 
-from common import DeviceTestEnv
-from common import FatalError
-from common import GetEnvVariableOrError
-from common import HostTestEnv
-from common import RetCode
+sys.path.append(os.path.dirname(os.path.dirname(
+        os.path.realpath(__file__))))
+
+from common.common import DeviceTestEnv
+from common.common import FatalError
+from common.common import GetEnvVariableOrError
+from common.common import HostTestEnv
+from common.common import LogSeverity
+from common.common import RetCode
 
 
 # Passes that are never disabled during search process because disabling them
@@ -57,6 +63,9 @@
 # position in the command.
 RAW_CMD_RUNTIME_ARGS_TAG = '{ARGS}'
 
+# Default core image path relative to ANDROID_HOST_OUT.
+DEFAULT_IMAGE_RELATIVE_PATH = '/framework/core.art'
+
 class Dex2OatWrapperTestable(object):
   """Class representing a testable compilation.
 
@@ -104,10 +113,9 @@
       print('Testing methods: {0} passes: {1}.'.format(
           compiled_methods, passes_to_run))
     cmd = self._PrepareCmd(compiled_methods=compiled_methods,
-                           passes_to_run=passes_to_run,
-                           verbose_compiler=False)
+                           passes_to_run=passes_to_run)
     (output, ret_code) = self._test_env.RunCommand(
-        cmd, {'ANDROID_LOG_TAGS': '*:e'})
+        cmd, LogSeverity.ERROR)
     res = True
     if self._expected_retcode:
       res = self._expected_retcode == ret_code
@@ -126,8 +134,8 @@
     Raises:
       FatalError: An error occurred when retrieving methods list.
     """
-    cmd = self._PrepareCmd(verbose_compiler=True)
-    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    cmd = self._PrepareCmd()
+    (output, _) = self._test_env.RunCommand(cmd, LogSeverity.INFO)
     match_methods = re.findall(r'Building ([^\n]+)\n', output)
     if not match_methods:
       raise FatalError('Failed to retrieve methods list. '
@@ -146,17 +154,15 @@
     Raises:
       FatalError: An error occurred when retrieving passes list.
     """
-    cmd = self._PrepareCmd(compiled_methods=[compiled_method],
-                           verbose_compiler=True)
-    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    cmd = self._PrepareCmd(compiled_methods=[compiled_method])
+    (output, _) = self._test_env.RunCommand(cmd, LogSeverity.INFO)
     match_passes = re.findall(r'Starting pass: ([^\n]+)\n', output)
     if not match_passes:
       raise FatalError('Failed to retrieve passes list. '
                        'Not recognized output format.')
     return [p for p in match_passes if p not in NON_PASSES]
 
-  def _PrepareCmd(self, compiled_methods=None, passes_to_run=None,
-                  verbose_compiler=False):
+  def _PrepareCmd(self, compiled_methods=None, passes_to_run=None):
     """Prepare command to run."""
     cmd = self._base_cmd[0:self._arguments_position]
     # insert additional arguments before the first argument
@@ -168,9 +174,8 @@
       self._test_env.WriteLines(self._passes_to_run_path, passes_to_run)
       cmd += ['-Xcompiler-option', '--run-passes={0}'.format(
           self._passes_to_run_path)]
-    if verbose_compiler:
-      cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
-              '-verbose:compiler', '-Xcompiler-option', '-j1']
+    cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
+            '-verbose:compiler', '-Xcompiler-option', '-j1']
     cmd += self._base_cmd[self._arguments_position:]
     return cmd
 
@@ -289,8 +294,7 @@
   if faulty_method_idx == len(all_methods) + 1:
     return (None, None)
   if faulty_method_idx == 0:
-    raise FatalError('Testable fails with no methods compiled. '
-                     'Perhaps issue lies outside of compiler.')
+    raise FatalError('Testable fails with no methods compiled.')
   faulty_method = all_methods[faulty_method_idx - 1]
   all_passes = testable.GetAllPassesForMethod(faulty_method)
   faulty_pass_idx = BinarySearch(
@@ -361,8 +365,8 @@
     if not args.device:
       base_cmd += ['-XXlib:{0}'.format(args.lib)]
       if not args.image:
-        image_path = '{0}/framework/core-optimizing-pic.art'.format(
-            GetEnvVariableOrError('ANDROID_HOST_OUT'))
+        image_path = (GetEnvVariableOrError('ANDROID_HOST_OUT') +
+                      DEFAULT_IMAGE_RELATIVE_PATH)
       else:
         image_path = args.image
       base_cmd += ['-Ximage:{0}'.format(image_path)]
@@ -410,7 +414,11 @@
   try:
     testable = Dex2OatWrapperTestable(base_cmd, test_env, args.expected_retcode,
                                       output_checker, args.verbose)
-    (method, opt_pass) = BugSearch(testable)
+    if testable.Test(compiled_methods=[]):
+      (method, opt_pass) = BugSearch(testable)
+    else:
+      print('Testable fails with no methods compiled.')
+      sys.exit(1)
   except Exception as e:
     print('Error occurred.\nLogfile: {0}'.format(test_env.logfile.name))
     test_env.logfile.write('Exception: {0}\n'.format(e))
diff --git a/tools/javafuzz/__init__.py b/tools/common/__init__.py
similarity index 100%
copy from tools/javafuzz/__init__.py
copy to tools/common/__init__.py
diff --git a/tools/bisection_search/common.py b/tools/common/common.py
similarity index 67%
rename from tools/bisection_search/common.py
rename to tools/common/common.py
index b69b606..b822dca 100755
--- a/tools/bisection_search/common.py
+++ b/tools/common/common.py
@@ -21,7 +21,12 @@
 import signal
 import shlex
 import shutil
+import time
 
+from enum import Enum
+from enum import unique
+
+from subprocess import DEVNULL
 from subprocess import check_call
 from subprocess import PIPE
 from subprocess import Popen
@@ -31,9 +36,6 @@
 from tempfile import mkdtemp
 from tempfile import NamedTemporaryFile
 
-from enum import Enum
-from enum import unique
-
 # Temporary directory path on device.
 DEVICE_TMP_PATH = '/data/local/tmp'
 
@@ -51,6 +53,48 @@
   NOTRUN = 4
 
 
+@unique
+class LogSeverity(Enum):
+  VERBOSE = 0
+  DEBUG = 1
+  INFO = 2
+  WARNING = 3
+  ERROR = 4
+  FATAL = 5
+  SILENT = 6
+
+  @property
+  def symbol(self):
+    return self.name[0]
+
+  @classmethod
+  def FromSymbol(cls, s):
+    for log_severity in LogSeverity:
+      if log_severity.symbol == s:
+        return log_severity
+    raise ValueError("{0} is not a valid log severity symbol".format(s))
+
+  def __ge__(self, other):
+    if self.__class__ is other.__class__:
+      return self.value >= other.value
+    return NotImplemented
+
+  def __gt__(self, other):
+    if self.__class__ is other.__class__:
+      return self.value > other.value
+    return NotImplemented
+
+  def __le__(self, other):
+    if self.__class__ is other.__class__:
+      return self.value <= other.value
+    return NotImplemented
+
+  def __lt__(self, other):
+    if self.__class__ is other.__class__:
+      return self.value < other.value
+    return NotImplemented
+
+
 def GetEnvVariableOrError(variable_name):
   """Gets value of an environmental variable.
 
@@ -72,6 +116,14 @@
   return top
 
 
+def GetJackClassPath():
+  """Returns Jack's classpath."""
+  top = GetEnvVariableOrError('ANDROID_BUILD_TOP')
+  libdir = top + '/out/host/common/obj/JAVA_LIBRARIES'
+  return libdir + '/core-libart-hostdex_intermediates/classes.jack:' \
+       + libdir + '/core-oj-hostdex_intermediates/classes.jack'
+
+
 def _DexArchCachePaths(android_data_path):
   """Returns paths to architecture specific caches.
 
@@ -116,23 +168,44 @@
   return (output, stderr_output, retcode)
 
 
-def _RunCommandForOutputAndLog(cmd, env, logfile, timeout=60):
-  """Runs command and logs its output. Returns the output.
+def _LogCmdOutput(logfile, cmd, output, retcode):
+  """Logs output of a command.
 
   Args:
-    cmd: list of strings, command to run.
-    env: shell environment to run the command with.
     logfile: file handle to logfile.
-    timeout: int, timeout in seconds.
-
-  Returns:
-    tuple (string, string, RetCode) stdout output, stderr output, normalized
-      return code.
+    cmd: list of strings, command.
+    output: command output.
+    retcode: RetCode, normalized retcode.
   """
-  (output, _, retcode) = RunCommandForOutput(cmd, env, PIPE, STDOUT, timeout)
   logfile.write('Command:\n{0}\n{1}\nReturn code: {2}\n'.format(
       CommandListToCommandString(cmd), output, retcode))
-  return (output, retcode)
+
+
+def RunCommand(cmd, out, err, timeout=5):
+  """Executes a command, and returns its return code.
+
+  Args:
+    cmd: list of strings, a command to execute
+    out: string, file name to open for stdout (or None)
+    err: string, file name to open for stderr (or None)
+    timeout: int, time out in seconds
+  Returns:
+    RetCode, return code of running command (forced RetCode.TIMEOUT
+    on timeout)
+  """
+  devnull = DEVNULL
+  outf = devnull
+  if out is not None:
+    outf = open(out, mode='w')
+  errf = devnull
+  if err is not None:
+    errf = open(err, mode='w')
+  (_, _, retcode) = RunCommandForOutput(cmd, None, outf, errf, timeout)
+  if outf != devnull:
+    outf.close()
+  if errf != devnull:
+    errf.close()
+  return retcode
 
 
 def CommandListToCommandString(cmd):
@@ -187,15 +260,14 @@
     """
 
   @abc.abstractmethod
-  def RunCommand(self, cmd, env_updates=None):
-    """Runs command in environment with updated environmental variables.
+  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
+    """Runs command in environment.
 
     Args:
       cmd: list of strings, command to run.
-      env_updates: dict, string to string, maps names of variables to their
-        updated values.
+      log_severity: LogSeverity, minimum severity of logs included in output.
     Returns:
-      tuple (string, string, int) stdout output, stderr output, return code.
+      tuple (string, int) output, return code.
     """
 
   @abc.abstractproperty
@@ -262,13 +334,17 @@
       f.writelines('{0}\n'.format(line) for line in lines)
     return
 
-  def RunCommand(self, cmd, env_updates=None):
-    if not env_updates:
-      env_updates = {}
+  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
     self._EmptyDexCache()
     env = self._shell_env.copy()
-    env.update(env_updates)
-    return _RunCommandForOutputAndLog(cmd, env, self._logfile, self._timeout)
+    env.update({'ANDROID_LOG_TAGS':'*:' + log_severity.symbol.lower()})
+    (output, err_output, retcode) = RunCommandForOutput(
+        cmd, env, PIPE, PIPE, self._timeout)
+    # We append err_output to output to stay consistent with DeviceTestEnv
+    # implementation.
+    output += err_output
+    _LogCmdOutput(self._logfile, cmd, output, retcode)
+    return (output, retcode)
 
   @property
   def logfile(self):
@@ -341,26 +417,63 @@
       self._AdbPush(temp_file.name, file_path)
     return
 
-  def RunCommand(self, cmd, env_updates=None):
-    if not env_updates:
-      env_updates = {}
+  def _ExtractPid(self, brief_log_line):
+    """Extracts PID from a single logcat line in brief format."""
+    pid_start_idx = brief_log_line.find('(') + 2
+    if pid_start_idx == -1:
+      return None
+    pid_end_idx = brief_log_line.find(')', pid_start_idx)
+    if pid_end_idx == -1:
+      return None
+    return brief_log_line[pid_start_idx:pid_end_idx]
+
+  def _ExtractSeverity(self, brief_log_line):
+    """Extracts LogSeverity from a single logcat line in brief format."""
+    if not brief_log_line:
+      return None
+    return LogSeverity.FromSymbol(brief_log_line[0])
+
+  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
     self._EmptyDexCache()
-    if 'ANDROID_DATA' not in env_updates:
-      env_updates['ANDROID_DATA'] = self._device_env_path
-    env_updates_cmd = ' '.join(['{0}={1}'.format(var, val) for var, val
-                                in env_updates.items()])
-    cmd = CommandListToCommandString(cmd)
-    adb = 'adb'
+    env_vars_cmd = 'ANDROID_DATA={0} ANDROID_LOG_TAGS=*:i'.format(
+        self._device_env_path)
+    adb_cmd = ['adb']
     if self._specific_device:
-      adb += ' -s ' + self._specific_device
-    cmd = '{0} shell "logcat -c && {1} {2}"'.format(
-        adb, env_updates_cmd, cmd)
-    (output, retcode) = _RunCommandForOutputAndLog(
-        shlex.split(cmd), self._shell_env, self._logfile, self._timeout)
-    logcat_cmd = 'adb shell "logcat -d -s -b main dex2oat:* dex2oatd:*"'
-    (err_output, _) = _RunCommandForOutputAndLog(
-        shlex.split(logcat_cmd), self._shell_env, self._logfile)
-    return (output + err_output, retcode)
+      adb_cmd += ['-s', self._specific_device]
+    logcat_cmd = adb_cmd + ['logcat', '-v', 'brief', '-s', '-b', 'main',
+                            '-T', '1', 'dex2oat:*', 'dex2oatd:*']
+    logcat_proc = Popen(logcat_cmd, stdout=PIPE, stderr=STDOUT,
+                        universal_newlines=True)
+    cmd_str = CommandListToCommandString(cmd)
+    # Print PID of the shell and exec command. We later retrieve this PID and
+    # use it to filter dex2oat logs, keeping those with matching parent PID.
+    device_cmd = ('echo $$ && ' + env_vars_cmd + ' exec ' + cmd_str)
+    cmd = adb_cmd + ['shell', device_cmd]
+    (output, _, retcode) = RunCommandForOutput(cmd, self._shell_env, PIPE,
+                                               STDOUT, self._timeout)
+    # We need to make sure to only kill logcat once all relevant logs arrive.
+    # Sleep is used for simplicity.
+    time.sleep(0.5)
+    logcat_proc.kill()
+    end_of_first_line = output.find('\n')
+    if end_of_first_line != -1:
+      parent_pid = output[:end_of_first_line]
+      output = output[end_of_first_line + 1:]
+      logcat_output, _ = logcat_proc.communicate()
+      logcat_lines = logcat_output.splitlines(keepends=True)
+      dex2oat_pids = []
+      for line in logcat_lines:
+        # Dex2oat was started by our runtime instance.
+        if 'Running dex2oat (parent PID = ' + parent_pid in line:
+          dex2oat_pids.append(self._ExtractPid(line))
+          break
+      if dex2oat_pids:
+        for line in logcat_lines:
+          if (self._ExtractPid(line) in dex2oat_pids and
+              self._ExtractSeverity(line) >= log_severity):
+            output += line
+    _LogCmdOutput(self._logfile, cmd, output, retcode)
+    return (output, retcode)
 
   @property
   def logfile(self):
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index 04bbbf8..7337ffc 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -22,6 +22,7 @@
 import dexfuzz.fuzzers.FuzzerSingleExecute;
 import dexfuzz.fuzzers.FuzzerSingleNoExecute;
 import dexfuzz.listeners.BaseListener;
+import dexfuzz.listeners.BisectionSearchListener;
 import dexfuzz.listeners.ConsoleLoggerListener;
 import dexfuzz.listeners.LogFileListener;
 import dexfuzz.listeners.MultiplexerListener;
@@ -66,6 +67,10 @@
       }
       // Add the file logging listener.
       multipleListener.addListener(new LogFileListener(Options.reportLogFile));
+      if (Options.runBisectionSearch) {
+        // Add the bisection search listener.
+        multipleListener.addListener(new BisectionSearchListener());
+      }
       // Add the unique program tracker.
       multipleListener.addListener(new UniqueProgramTrackerListener(Options.uniqueDatabaseFile));
       listener = multipleListener;
diff --git a/tools/dexfuzz/src/dexfuzz/ExecutionResult.java b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
index 3a8c6cb..f85af1a 100644
--- a/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
+++ b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
@@ -31,6 +31,7 @@
   private String flattenedError;
   private String flattenedErrorWithNewlines;
   private String flattenedAll;
+  private String flattenedAllWithNewlines;
 
   private static final int TIMEOUT_RETURN_VALUE = 124;
   private static final int SIGABORT_RETURN_VALUE = 134;
@@ -101,6 +102,16 @@
     return flattenedAll;
   }
 
+  /**
+   * Get both the output and error, concatenated together, including newline characters.
+   */
+  public String getFlattenedAllWithNewlines() {
+    if (flattenedAllWithNewlines == null) {
+      flattenedAllWithNewlines = getFlattenedOutputWithNewlines() + getFlattenedErrorWithNewlines();
+    }
+    return flattenedAllWithNewlines;
+  }
+
   public boolean isTimeout() {
     return (returnValue == TIMEOUT_RETURN_VALUE);
   }
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
index 2e929c8..b442b22 100644
--- a/tools/dexfuzz/src/dexfuzz/Options.java
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -78,6 +78,7 @@
   public static boolean skipMutation;
   public static boolean dumpMutations;
   public static boolean loadMutations;
+  public static boolean runBisectionSearch;
 
   /**
    * Print out usage information about dexfuzz, and then exit.
@@ -138,6 +139,7 @@
     Log.always("  --report-unique        : Print out information about unique programs generated");
     Log.always("  --unique-db=<file>     : Use <file> store results about unique programs");
     Log.always("                           (Default: unique_progs.db)");
+    Log.always("  --bisection-search     : Run bisection search for divergences");
     Log.always("");
     System.exit(0);
   }
@@ -197,6 +199,8 @@
       methodMutations = 1;
       minMethods = 1;
       maxMethods = 1;
+    } else if (flag.equals("bisection-search")) {
+      runBisectionSearch = true;
     } else if (flag.equals("help")) {
       usage();
     } else {
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
index 227c698..5546207 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
@@ -21,11 +21,12 @@
 public class Arm64InterpreterExecutor extends Executor {
 
   public Arm64InterpreterExecutor(BaseListener listener, Device device) {
-    super("ARM64 Interpreter", 30, listener, Architecture.ARM64, device, false);
+    super("ARM64 Interpreter", 30, listener, Architecture.ARM64, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xint ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
index bfa87b7..72e36e8 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -21,11 +21,12 @@
 public class Arm64OptimizingBackendExecutor extends Executor {
 
   public Arm64OptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("ARM64 Optimizing Backend", 5, listener, Architecture.ARM64, device, true);
+    super("ARM64 Optimizing Backend", 5, listener, Architecture.ARM64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
index 7251ec5..d9228ed 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
@@ -21,11 +21,12 @@
 public class Arm64QuickBackendExecutor extends Executor {
 
   public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device, true);
+    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
index d17ea87..bdfad3d 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
@@ -21,11 +21,12 @@
 public class ArmInterpreterExecutor extends Executor {
 
   public ArmInterpreterExecutor(BaseListener listener, Device device) {
-    super("ARM Interpreter", 30, listener, Architecture.ARM, device, false);
+    super("ARM Interpreter", 30, listener, Architecture.ARM, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xint ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
index 947bb2f..ded8cf9 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -21,11 +21,12 @@
 public class ArmOptimizingBackendExecutor extends Executor {
 
   public ArmOptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("ARM Optimizing Backend", 5, listener, Architecture.ARM, device, true);
+    super("ARM Optimizing Backend", 5, listener, Architecture.ARM, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
index 7d226e8..0eb35f7 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
@@ -21,11 +21,12 @@
 public class ArmQuickBackendExecutor extends Executor {
 
   public ArmQuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM Quick Backend", 5, listener, Architecture.ARM, device, true);
+    super("ARM Quick Backend", 5, listener, Architecture.ARM, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
     if (device.noBootImageAvailable()) {
@@ -33,6 +34,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
index 45538fe..72f73b8 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Device.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -18,7 +18,11 @@
 
 import java.io.IOException;
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import dexfuzz.ExecutionResult;
 import dexfuzz.Log;
@@ -139,6 +143,10 @@
     return isHost;
   }
 
+  public boolean isUsingSpecificDevice() {
+    return usingSpecificDevice;
+  }
+
   /**
    * Certain AOSP builds of Android may not have a full boot.art built. This will be set if
    * we use --no-boot-image, and is used by Executors when deciding the arguments for dalvikvm
@@ -186,7 +194,7 @@
     Log.info("Executing: " + command);
 
     try {
-      ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+      ProcessBuilder processBuilder = new ProcessBuilder(splitCommand(command));
       processBuilder.environment().put("ANDROID_ROOT", androidHostOut);
       if (Options.executeOnHost) {
         processBuilder.environment().put("ANDROID_DATA", androidData);
@@ -229,6 +237,17 @@
     return result;
   }
 
+  /**
+   * Splits command respecting single quotes.
+   */
+  private List<String> splitCommand(String command) {
+    List<String> ret = new ArrayList<String>();
+    Matcher m = Pattern.compile("(\'[^\']+\'| *[^ ]+ *)").matcher(command);
+    while (m.find())
+      ret.add(m.group(1).trim().replace("\'", ""));
+    return ret;
+  }
+
   private String getExecutionPrefixWithAdb(String command) {
     if (usingSpecificDevice) {
       return String.format("adb -s %s %s ", deviceName, command);
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
index 1e5d4be..c62a3ad 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -39,9 +39,10 @@
   protected Architecture architecture;
   protected Device device;
   private boolean needsCleanCodeCache;
+  private boolean isBisectable;
 
   protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
-      Device device, boolean needsCleanCodeCache) {
+      Device device, boolean needsCleanCodeCache, boolean isBisectable) {
     executeClass = Options.executeClass;
 
     if (Options.shortTimeouts) {
@@ -55,6 +56,7 @@
     this.architecture = architecture;
     this.device = device;
     this.needsCleanCodeCache = needsCleanCodeCache;
+    this.isBisectable = isBisectable;
 
     if (Options.executeOnHost) {
       this.testLocation = System.getProperty("user.dir");
@@ -169,7 +171,33 @@
    * Executor subclasses need to override this, to construct their arguments for dalvikvm
    * invocation correctly.
    */
-  public abstract void execute(String programName);
+  protected abstract String constructCommand(String programName);
+
+  /**
+   * Executes runtime.
+   */
+  public void execute(String programName) {
+    executionResult = executeCommandWithTimeout(constructCommand(programName), true);
+  }
+
+  /**
+   * Runs bisection bug search.
+   */
+  public ExecutionResult runBisectionSearch(String programName, String expectedOutputFile, String logFile) {
+    assert(isBisectable);
+    String runtimeCommand = constructCommand(programName);
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("bisection_search.py --raw-cmd '").append(runtimeCommand);
+    commandBuilder.append("' --expected-output=").append(expectedOutputFile);
+    commandBuilder.append(" --logfile=").append(logFile);
+    if (!device.isHost()) {
+      commandBuilder.append(" --device");
+      if (device.isUsingSpecificDevice()) {
+        commandBuilder.append(" --specific-device=").append(device.getName());
+      }
+    }
+    return device.executeCommand(commandBuilder.toString(), true, outputConsumer, errorConsumer);
+  }
 
   /**
    * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
@@ -207,4 +235,8 @@
   public void finishedWithProgramOnDevice() {
     device.resetProgramPushed();
   }
+
+  public boolean isBisectable() {
+    return isBisectable;
+  }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
index f319201..eee6111 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
@@ -21,16 +21,17 @@
 public class Mips64InterpreterExecutor extends Executor {
 
   public Mips64InterpreterExecutor(BaseListener listener, Device device) {
-    super("MIPS64 Interpreter", 30, listener, Architecture.MIPS64, device, false);
+    super("MIPS64 Interpreter", 30, listener, Architecture.MIPS64, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xint ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
 
   }
-}
\ No newline at end of file
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
index a6784e6..72d43e7 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -21,15 +21,16 @@
 public class Mips64OptimizingBackendExecutor extends Executor {
 
   public Mips64OptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS64 Optimizing Backend", 5, listener, Architecture.MIPS64, device, true);
+    super("MIPS64 Optimizing Backend", 5, listener, Architecture.MIPS64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
index 36e39c2..e7e5ff6 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
@@ -21,15 +21,16 @@
 public class Mips64QuickBackendExecutor extends Executor {
 
   public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device, true);
+    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
index 4268c76..4a403db 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
@@ -21,15 +21,16 @@
 public class MipsInterpreterExecutor extends Executor {
 
   public MipsInterpreterExecutor(BaseListener listener, Device device) {
-    super("MIPS Interpreter", 30, listener, Architecture.MIPS, device, false);
+    super("MIPS Interpreter", 30, listener, Architecture.MIPS, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xint ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
index d64b1ce..63f6858 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -21,15 +21,16 @@
 public class MipsOptimizingBackendExecutor extends Executor {
 
   public MipsOptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS Optimizing Backend", 5, listener, Architecture.MIPS, device, true);
+    super("MIPS Optimizing Backend", 5, listener, Architecture.MIPS, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
index 0ea166b..b262090 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
@@ -21,15 +21,16 @@
 public class MipsQuickBackendExecutor extends Executor {
 
   public MipsQuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device, true);
+    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
index 510f0d0..a8e68a7 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
@@ -22,11 +22,12 @@
 public class X86InterpreterExecutor extends Executor {
 
   public X86InterpreterExecutor(BaseListener listener, Device device) {
-    super("x86 Interpreter", 30, listener, Architecture.X86, device, false);
+    super("x86 Interpreter", 30, listener, Architecture.X86, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xint ");
     if (Options.executeOnHost) {
@@ -34,6 +35,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
index 81d7285..5908a8b 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -22,11 +22,12 @@
 public class X86OptimizingBackendExecutor extends Executor {
 
   public X86OptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("x86 Optimizing Backend", 5, listener, Architecture.X86, device, true);
+    super("x86 Optimizing Backend", 5, listener, Architecture.X86, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
     if (Options.executeOnHost) {
@@ -34,6 +35,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
index 7e4a2f6..9e8039d 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
@@ -22,11 +22,12 @@
 public class X86QuickBackendExecutor extends Executor {
 
   public X86QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86 Quick Backend", 5, listener, Architecture.X86, device, true);
+    super("x86 Quick Backend", 5, listener, Architecture.X86, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
     if (Options.executeOnHost) {
@@ -34,6 +35,6 @@
     }
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
index dc55a41..af00760 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
@@ -21,15 +21,16 @@
 public class X86_64InterpreterExecutor extends Executor {
 
   public X86_64InterpreterExecutor(BaseListener listener, Device device) {
-    super("x86_64 Interpreter", 30, listener, Architecture.X86_64, device, false);
+    super("x86_64 Interpreter", 30, listener, Architecture.X86_64, device,
+        /*needsCleanCodeCache*/ false, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xint ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
index 2a01c6c..28ff1a5 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -21,15 +21,16 @@
 public class X86_64OptimizingBackendExecutor extends Executor {
 
   public X86_64OptimizingBackendExecutor(BaseListener listener, Device device) {
-    super("x86_64 Optimizing Backend", 5, listener, Architecture.X86_64, device, true);
+    super("x86_64 Optimizing Backend", 5, listener, Architecture.X86_64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ true);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
index 995cba2..22cafe2 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
@@ -21,15 +21,16 @@
 public class X86_64QuickBackendExecutor extends Executor {
 
   public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device, true);
+    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device,
+        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
   }
 
   @Override
-  public void execute(String programName) {
+  protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
-    executionResult = executeCommandWithTimeout(commandBuilder.toString(), true);
+    return commandBuilder.toString();
   }
 }
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/BisectionSearchListener.java b/tools/dexfuzz/src/dexfuzz/listeners/BisectionSearchListener.java
new file mode 100644
index 0000000..dfd9637
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/BisectionSearchListener.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Runs bisection search for divergent programs.
+ */
+public class BisectionSearchListener extends BaseListener {
+
+  /**
+   * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this
+   * seed as a name, if we find a divergence.
+   */
+  private long currentSeed;
+
+  /**
+   * Used to remember the name of the file we've fuzzed, so we can save it if we
+   * find a divergence.
+   */
+  private String fuzzedFile;
+
+  @Override
+  public void handleSeed(long seed) {
+    currentSeed = seed;
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    fuzzedFile = programName;
+  }
+
+  private void writeToFile(String file, String toWrite) throws IOException {
+    PrintWriter writer = new PrintWriter(file);
+    writer.write(toWrite);
+    writer.close();
+  }
+
+  private String extractExpectedOutput(ExecutionResult result) {
+    StringBuilder builder = new StringBuilder();
+    // Skip last, artificial output line with return code.
+    for (int i = 0; i < result.output.size() - 1; i++) {
+      builder.append(result.output.get(i)).append("\n");
+    }
+    return builder.toString();
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    if (outputMap.size() != 2) {
+      // It's unclear which output should be considered reference output.
+      return;
+    }
+    try {
+      File expected_output_file = File.createTempFile("expected_output", ".txt");
+      String outputFile = String.format("bisection_outputs/%d_out.txt", currentSeed);
+      String logFile = String.format("bisection_outputs/%d_log.txt", currentSeed);
+      List<List<Executor>> executorsGroupedByOutput =
+          new ArrayList<List<Executor>>(outputMap.values());
+      List<String> outputs = new ArrayList<String>();
+      for (List<Executor> executors : executorsGroupedByOutput) {
+        outputs.add(extractExpectedOutput(executors.get(0).getResult()));
+      }
+      for (int i = 0; i < 2; i++) {
+        String output = outputs.get(i);
+        String otherOutput = outputs.get(1 - i);
+        List<Executor> executors = executorsGroupedByOutput.get(i);
+        for (Executor executor : executors) {
+          if (executor.isBisectable()) {
+            writeToFile(expected_output_file.getAbsolutePath(), otherOutput);
+            ExecutionResult result = executor.runBisectionSearch(fuzzedFile,
+                expected_output_file.getAbsolutePath(), logFile);
+            writeToFile(outputFile, result.getFlattenedAllWithNewlines());
+          }
+        }
+      }
+      expected_output_file.delete();
+    } catch (IOException e) {
+      Log.error(
+          "BisectionSearchListener.handleDivergences() caught an IOException " + e.toString());
+    }
+  }
+
+}
diff --git a/tools/javafuzz/Android.mk b/tools/jfuzz/Android.mk
similarity index 90%
rename from tools/javafuzz/Android.mk
rename to tools/jfuzz/Android.mk
index 63db57a..c7002d6 100644
--- a/tools/javafuzz/Android.mk
+++ b/tools/jfuzz/Android.mk
@@ -12,14 +12,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Java fuzzer tool.
+# Fuzzer tool.
 
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := javafuzz.cc
+LOCAL_SRC_FILES := jfuzz.cc
 LOCAL_CFLAGS += -O0 -g -Wall
 LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_MODULE := javafuzz
+LOCAL_MODULE := jfuzz
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/javafuzz/README.md b/tools/jfuzz/README.md
similarity index 63%
rename from tools/javafuzz/README.md
rename to tools/jfuzz/README.md
index b08075a..c87e714 100644
--- a/tools/javafuzz/README.md
+++ b/tools/jfuzz/README.md
@@ -1,20 +1,20 @@
-JavaFuzz
-========
+JFuzz
+=====
 
-JavaFuzz is a tool for generating random Java programs with the objective
-of fuzz testing the ART infrastructure. Each randomly generated Java program
+JFuzz is a tool for generating random programs with the objective
+of fuzz testing the ART infrastructure. Each randomly generated program
 can be run under various modes of execution, such as using the interpreter,
 using the optimizing compiler, using an external reference implementation,
 or using various target architectures. Any difference between the outputs
 (**divergence**) may indicate a bug in one of the execution modes.
 
-JavaFuzz can be combined with dexfuzz to get multi-layered fuzz testing.
+JFuzz can be combined with DexFuzz to get multi-layered fuzz testing.
 
-How to run JavaFuzz
-===================
+How to run JFuzz
+================
 
-    javafuzz [-s seed] [-d expr-depth] [-l stmt-length]
-             [-i if-nest] [-n loop-nest]
+    jfuzz [-s seed] [-d expr-depth] [-l stmt-length]
+             [-i if-nest] [-n loop-nest] [-v] [-h]
 
 where
 
@@ -28,33 +28,67 @@
          (higher values yield deeper nested conditionals)
     -n : defines a fuzzing nest for for/while/do-while loops
          (higher values yield deeper nested loops)
+    -v : prints version number and exits
+    -h : prints help and exits
 
-The current version of JavaFuzz sends all output to stdout, and uses
+The current version of JFuzz sends all output to stdout, and uses
 a fixed testing class named Test. So a typical test run looks as follows.
 
-    javafuzz > Test.java
+    jfuzz > Test.java
     jack -cp ${JACK_CLASSPATH} --output-dex . Test.java
     art -classpath classes.dex Test
 
-How to start the JavaFuzz tests
-===============================
+How to start JFuzz testing
+==========================
 
-    run_java_fuzz_test.py
-                          [--num_tests=#TESTS]
+    run_jfuzz_test.py
+                          [--num_tests=NUM_TESTS]
                           [--device=DEVICE]
                           [--mode1=MODE] [--mode2=MODE]
+                          [--report_script=SCRIPT]
+                          [--jfuzz_arg=ARG]
+                          [--true_divergence]
 
 where
 
-    --num_tests : number of tests to run (10000 by default)
-    --device    : target device serial number (passed to adb -s)
-    --mode1     : m1
-    --mode2     : m2, with m1 != m2, and values one of
+    --num_tests       : number of tests to run (10000 by default)
+    --device          : target device serial number (passed to adb -s)
+    --mode1           : m1
+    --mode2           : m2, with m1 != m2, and values one of
       ri   = reference implementation on host (default for m1)
       hint = Art interpreter on host
       hopt = Art optimizing on host (default for m2)
       tint = Art interpreter on target
       topt = Art optimizing on target
+    --report_script   : path to script called for each divergence
+    --jfuzz_arg       : argument for jfuzz
+    --true_divergence : don't bisect timeout divergences
+
+How to start JFuzz nightly testing
+==================================
+
+    run_jfuzz_test_nightly.py
+                          [--num_proc NUM_PROC]
+
+where
+
+    --num_proc      : number of run_jfuzz_test.py instances to run (8 by default)
+
+Remaining arguments are passed to run\_jfuzz_test.py.
+
+How to start J/DexFuzz testing (multi-layered)
+==============================================
+
+    run_dex_fuzz_test.py
+                          [--num_tests=NUM_TESTS]
+                          [--num_inputs=NUM_INPUTS]
+                          [--device=DEVICE]
+
+where
+
+    --num_tests : number of tests to run (10000 by default)
+    --num_inputs: number of JFuzz programs to generate
+    --device    : target device serial number (passed to adb -s)
 
 Background
 ==========
diff --git a/tools/javafuzz/__init__.py b/tools/jfuzz/__init__.py
similarity index 100%
rename from tools/javafuzz/__init__.py
rename to tools/jfuzz/__init__.py
diff --git a/tools/javafuzz/javafuzz.cc b/tools/jfuzz/jfuzz.cc
similarity index 88%
rename from tools/javafuzz/javafuzz.cc
rename to tools/jfuzz/jfuzz.cc
index 161ae0a..82683f2 100644
--- a/tools/javafuzz/javafuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -21,12 +21,13 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <time.h>
+
+#include <sys/time.h>
 
 namespace {
 
 /*
- * Java operators.
+ * Operators.
  */
 
 #define EMIT(x) fputs((x)[random0(sizeof(x)/sizeof(const char*))], out_);
@@ -49,33 +50,34 @@
 static constexpr const char* kRelOps[]     = { "==", "!=", ">", ">=", "<", "<=" };
 
 /*
- * Version of JavaFuzz. Increase this each time changes are made to the program
- * to preserve the property that a given version of JavaFuzz yields the same
- * fuzzed Java program for a deterministic random seed.
+ * Version of JFuzz. Increase this each time changes are made to the program
+ * to preserve the property that a given version of JFuzz yields the same
+ * fuzzed program for a deterministic random seed.
  */
-const char* VERSION = "1.1";
+const char* VERSION = "1.2";
 
-static const uint32_t MAX_DIMS[11] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 };
+/*
+ * Maximum number of array dimensions, together with corresponding maximum size
+ * within each dimension (to keep memory/runtime requirements roughly the same).
+ */
+static const uint32_t kMaxDim = 10;
+static const uint32_t kMaxDimSize[kMaxDim + 1] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 };
 
 /**
- * A class that generates a random Java program that compiles correctly. The program
+ * A class that generates a random program that compiles correctly. The program
  * is generated using rules that generate various programming constructs. Each rule
  * has a fixed probability to "fire". Running a generated program yields deterministic
  * output, making it suited to test various modes of execution (e.g an interpreter vs.
  * an compiler or two different run times) for divergences.
- *
- * TODO: Due to the original scope of this project, the generated Java program is heavy
- *       on loops, arrays, and basic operations; fuzzing other aspects of Java programs,
- *       like elaborate typing, class hierarchies, and interfaces is still TBD.
  */
-class JavaFuzz {
+class JFuzz {
  public:
-  JavaFuzz(FILE* out,
-           uint32_t seed,
-           uint32_t expr_depth,
-           uint32_t stmt_length,
-           uint32_t if_nest,
-           uint32_t loop_nest)
+  JFuzz(FILE* out,
+        uint32_t seed,
+        uint32_t expr_depth,
+        uint32_t stmt_length,
+        uint32_t if_nest,
+        uint32_t loop_nest)
       : out_(out),
         fuzz_random_engine_(seed),
         fuzz_seed_(seed),
@@ -85,8 +87,8 @@
         fuzz_loop_nest_(loop_nest),
         return_type_(randomType()),
         array_type_(randomType()),
-        array_dim_(random1(10)),
-        array_size_(random1(MAX_DIMS[array_dim_])),
+        array_dim_(random1(kMaxDim)),
+        array_size_(random1(kMaxDimSize[array_dim_])),
         indentation_(0),
         expr_depth_(0),
         stmt_length_(0),
@@ -98,9 +100,10 @@
         int_local_(0),
         long_local_(0),
         float_local_(0),
-        double_local_(0) { }
+        double_local_(0),
+        in_inner_(false) { }
 
-  ~JavaFuzz() { }
+  ~JFuzz() { }
 
   void emitProgram() {
     emitHeader();
@@ -378,6 +381,27 @@
     }
   }
 
+  // Emit a method call (out type given).
+  void emitMethodCall(Type tp) {
+    if (tp != kBoolean && !in_inner_) {
+      // Accept all numerical types (implicit conversion) and when not
+      // declaring inner classes (to avoid infinite recursion).
+      switch (random1(8)) {
+        case 1: fputs("mA.a()",  out_); break;
+        case 2: fputs("mB.a()",  out_); break;
+        case 3: fputs("mB.x()",  out_); break;
+        case 4: fputs("mBX.x()", out_); break;
+        case 5: fputs("mC.s()",  out_); break;
+        case 6: fputs("mC.c()",  out_); break;
+        case 7: fputs("mC.x()",  out_); break;
+        case 8: fputs("mCX.x()", out_); break;
+      }
+    } else {
+      // Fall back to intrinsic.
+      emitIntrinsic(tp);
+    }
+  }
+
   // Emit unboxing boxed object.
   void emitUnbox(Type tp) {
     fputc('(', out_);
@@ -392,7 +416,7 @@
   // Emit miscellaneous constructs.
   void emitMisc(Type tp) {
     if (tp == kBoolean) {
-      fputs("this instanceof Test", out_);
+      fprintf(out_, "this instanceof %s", in_inner_ ? "X" : "Test");
     } else if (isInteger(tp)) {
       const char* prefix = tp == kLong ? "Long" : "Integer";
       switch (random1(2)) {
@@ -572,10 +596,14 @@
         emitIntrinsic(tp);
         break;
       case 7:
+        // Method call: mA.a()
+        emitMethodCall(tp);
+        break;
+      case 8:
         // Emit unboxing boxed value: (int) Integer(x)
         emitUnbox(tp);
         break;
-      case 8:
+      case 9:
         // Miscellaneous constructs: a.length
         emitMisc(tp);
         break;
@@ -870,8 +898,52 @@
     return true;
   }
 
+  // Emit interface and class declarations.
+  void emitClassDecls() {
+    in_inner_ = true;
+    fputs("  private interface X {\n", out_);
+    fputs("    int x();\n", out_);
+    fputs("  }\n\n", out_);
+    fputs("  private class A {\n", out_);
+    fputs("    public int a() {\n", out_);
+    fputs("      return ", out_);
+    emitExpression(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("  }\n\n", out_);
+    fputs("  private class B extends A implements X {\n", out_);
+    fputs("    public int a() {\n", out_);
+    fputs("      return super.a() + ", out_);
+    emitExpression(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("    public int x() {\n", out_);
+    fputs("      return ", out_);
+    emitExpression(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("  }\n\n", out_);
+    fputs("  private static class C implements X {\n", out_);
+    fputs("    public static int s() {\n", out_);
+    fputs("      return ", out_);
+    emitLiteral(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("    public int c() {\n", out_);
+    fputs("      return ", out_);
+    emitLiteral(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("    public int x() {\n", out_);
+    fputs("      return ", out_);
+    emitLiteral(kInt);
+    fputs(";\n    }\n", out_);
+    fputs("  }\n\n", out_);
+    in_inner_ = false;
+  }
+
   // Emit field declarations.
   void emitFieldDecls() {
+    fputs("  private A mA  = new B();\n", out_);
+    fputs("  private B mB  = new B();\n", out_);
+    fputs("  private X mBX = new B();\n", out_);
+    fputs("  private C mC  = new C();\n", out_);
+    fputs("  private X mCX = new C();\n\n", out_);
     fputs("  private boolean mZ = false;\n", out_);
     fputs("  private int     mI = 0;\n", out_);
     fputs("  private long    mJ = 0;\n", out_);
@@ -978,10 +1050,10 @@
 
   // Emit program header. Emit command line options in the comments.
   void emitHeader() {
-    fputs("\n/**\n * AOSP Java Fuzz Tester.\n", out_);
-    fputs(" * Automatically generated Java program.\n", out_);
+    fputs("\n/**\n * AOSP JFuzz Tester.\n", out_);
+    fputs(" * Automatically generated program.\n", out_);
     fprintf(out_,
-            " * javafuzz -s %u -d %u -l %u -i %u -n %u (version %s)\n */\n\n",
+            " * jfuzz -s %u -d %u -l %u -i %u -n %u (version %s)\n */\n\n",
             fuzz_seed_,
             fuzz_expr_depth_,
             fuzz_stmt_length_,
@@ -995,6 +1067,7 @@
   void emitTestClassWithMain() {
     fputs("public class Test {\n\n", out_);
     indentation_ += 2;
+    emitClassDecls();
     emitFieldDecls();
     emitArrayDecl();
     emitTestConstructor();
@@ -1053,13 +1126,18 @@
   uint32_t long_local_;
   uint32_t float_local_;
   uint32_t double_local_;
+  bool in_inner_;
 };
 
 }  // anonymous namespace
 
 int32_t main(int32_t argc, char** argv) {
+  // Time-based seed.
+  struct timeval tp;
+  gettimeofday(&tp, NULL);
+
   // Defaults.
-  uint32_t seed = time(NULL);
+  uint32_t seed = (tp.tv_sec * 1000000 + tp.tv_usec);
   uint32_t expr_depth = 1;
   uint32_t stmt_length = 8;
   uint32_t if_nest = 2;
@@ -1067,7 +1145,7 @@
 
   // Parse options.
   while (1) {
-    int32_t option = getopt(argc, argv, "s:d:l:i:n:h");
+    int32_t option = getopt(argc, argv, "s:d:l:i:n:vh");
     if (option < 0) {
       break;  // done
     }
@@ -1087,12 +1165,15 @@
       case 'n':
         loop_nest = strtoul(optarg, nullptr, 0);
         break;
+      case 'v':
+        fprintf(stderr, "jfuzz version %s\n", VERSION);
+        return 0;
       case 'h':
       default:
         fprintf(stderr,
                 "usage: %s [-s seed] "
                 "[-d expr-depth] [-l stmt-length] "
-                "[-i if-nest] [-n loop-nest] [-h]\n",
+                "[-i if-nest] [-n loop-nest] [-v] [-h]\n",
                 argv[0]);
         return 1;
     }
@@ -1101,8 +1182,8 @@
   // Seed global random generator.
   srand(seed);
 
-  // Generate fuzzed Java program.
-  JavaFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest);
+  // Generate fuzzed program.
+  JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest);
   fuzz.emitProgram();
   return 0;
 }
diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py
new file mode 100755
index 0000000..56cdf02
--- /dev/null
+++ b/tools/jfuzz/run_dex_fuzz_test.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 argparse
+import os
+import shutil
+import sys
+
+from subprocess import check_call
+from tempfile import mkdtemp
+
+sys.path.append(os.path.dirname(os.path.dirname(
+        os.path.realpath(__file__))))
+
+from common.common import FatalError
+from common.common import GetJackClassPath
+from common.common import RetCode
+from common.common import RunCommand
+
+
+#
+# Tester class.
+#
+
+
+class DexFuzzTester(object):
+  """Tester that feeds JFuzz programs into DexFuzz testing."""
+
+  def  __init__(self, num_tests, num_inputs, device):
+    """Constructor for the tester.
+
+    Args:
+      num_tests: int, number of tests to run
+      num_inputs: int, number of JFuzz programs to generate
+      device: string, target device serial number (or None)
+    """
+    self._num_tests = num_tests
+    self._num_inputs = num_inputs
+    self._device = device
+    self._save_dir = None
+    self._results_dir = None
+    self._dexfuzz_dir = None
+    self._inputs_dir = None
+
+  def __enter__(self):
+    """On entry, enters new temp directory after saving current directory.
+
+    Raises:
+      FatalError: error when temp directory cannot be constructed
+    """
+    self._save_dir = os.getcwd()
+    self._results_dir = mkdtemp(dir='/tmp/')
+    self._dexfuzz_dir = mkdtemp(dir=self._results_dir)
+    self._inputs_dir = mkdtemp(dir=self._dexfuzz_dir)
+    if self._results_dir is None or self._dexfuzz_dir is None or \
+        self._inputs_dir is None:
+      raise FatalError('Cannot obtain temp directory')
+    os.chdir(self._dexfuzz_dir)
+    return self
+
+  def __exit__(self, etype, evalue, etraceback):
+    """On exit, re-enters previously saved current directory and cleans up."""
+    os.chdir(self._save_dir)
+    # TODO: detect divergences or shutil.rmtree(self._results_dir)
+
+  def Run(self):
+    """Feeds JFuzz programs into DexFuzz testing."""
+    print()
+    print('**\n**** JFuzz Testing\n**')
+    print()
+    print('#Tests    :', self._num_tests)
+    print('Device    :', self._device)
+    print('Directory :', self._results_dir)
+    print()
+    self.GenerateJFuzzPrograms()
+    self.RunDexFuzz()
+
+
+  def GenerateJFuzzPrograms(self):
+    """Generates JFuzz programs.
+
+    Raises:
+      FatalError: error when generation fails
+    """
+    os.chdir(self._inputs_dir)
+    for i in range(1, self._num_inputs + 1):
+      jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
+      if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS:
+        raise FatalError('Unexpected error while running JFuzz')
+      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
+                    timeout=30) != RetCode.SUCCESS:
+        raise FatalError('Unexpected error while running Jack')
+      shutil.move('Test.java', '../Test' + str(i) + '.java')
+      shutil.move('classes.dex', 'classes' + str(i) + '.dex')
+    os.unlink('jackerr.txt')
+
+  def RunDexFuzz(self):
+    """Starts the DexFuzz testing."""
+    os.chdir(self._dexfuzz_dir)
+    os.environ['ANDROID_DATA'] = self._dexfuzz_dir
+    dexfuzz_args = ['--inputs=' + self._inputs_dir, '--execute',
+                    '--execute-class=Test', '--repeat=' + str(self._num_tests),
+                    '--dump-output', '--interpreter', '--optimizing']
+    if self._device is not None:
+      dexfuzz_args += ['--device=' + self._device, '--allarm']
+    else:
+      dexfuzz_args += ['--host']  # Assume host otherwise.
+    check_call(['dexfuzz'] + dexfuzz_args)
+    # TODO: summarize findings.
+
+
+def main():
+  # Handle arguments.
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--num_tests', default=10000,
+                      type=int, help='number of tests to run')
+  parser.add_argument('--num_inputs', default=50,
+                      type=int, help='number of JFuzz program to generate')
+  parser.add_argument('--device', help='target device serial number')
+  args = parser.parse_args()
+  # Run the DexFuzz tester.
+  with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer:
+    fuzzer.Run()
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/javafuzz/run_java_fuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
similarity index 73%
rename from tools/javafuzz/run_java_fuzz_test.py
rename to tools/jfuzz/run_jfuzz_test.py
index 51d00be..fd8415d 100755
--- a/tools/javafuzz/run_java_fuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -17,69 +17,31 @@
 import abc
 import argparse
 import filecmp
-
-from glob import glob
-
 import os
 import shlex
 import shutil
 import subprocess
 import sys
 
+from glob import glob
+from subprocess import DEVNULL
 from tempfile import mkdtemp
 
 sys.path.append(os.path.dirname(os.path.dirname(
     os.path.realpath(__file__))))
 
-from bisection_search.common import RetCode
-from bisection_search.common import CommandListToCommandString
-from bisection_search.common import FatalError
-from bisection_search.common import GetEnvVariableOrError
-from bisection_search.common import RunCommandForOutput
-from bisection_search.common import DeviceTestEnv
+from common.common import RetCode
+from common.common import CommandListToCommandString
+from common.common import FatalError
+from common.common import GetJackClassPath
+from common.common import GetEnvVariableOrError
+from common.common import RunCommand
+from common.common import RunCommandForOutput
+from common.common import DeviceTestEnv
 
 # Return codes supported by bisection bug search.
 BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT)
 
-#
-# Utility methods.
-#
-
-
-def RunCommand(cmd, out, err, timeout=5):
-  """Executes a command, and returns its return code.
-
-  Args:
-    cmd: list of strings, a command to execute
-    out: string, file name to open for stdout (or None)
-    err: string, file name to open for stderr (or None)
-    timeout: int, time out in seconds
-  Returns:
-    RetCode, return code of running command (forced RetCode.TIMEOUT
-    on timeout)
-  """
-  devnull = subprocess.DEVNULL
-  outf = devnull
-  if out is not None:
-    outf = open(out, mode='w')
-  errf = devnull
-  if err is not None:
-    errf = open(err, mode='w')
-  (_, _, retcode) = RunCommandForOutput(cmd, None, outf, errf, timeout)
-  if outf != devnull:
-    outf.close()
-  if errf != devnull:
-    errf.close()
-  return retcode
-
-
-def GetJackClassPath():
-  """Returns Jack's classpath."""
-  top = GetEnvVariableOrError('ANDROID_BUILD_TOP')
-  libdir = top + '/out/host/common/obj/JAVA_LIBRARIES'
-  return libdir + '/core-libart-hostdex_intermediates/classes.jack:' \
-       + libdir + '/core-oj-hostdex_intermediates/classes.jack'
-
 
 def GetExecutionModeRunner(device, mode):
   """Returns a runner for the given execution mode.
@@ -104,6 +66,7 @@
     return TestRunnerArtOptOnTarget(device)
   raise FatalError('Unknown execution mode')
 
+
 #
 # Execution mode classes.
 #
@@ -248,7 +211,7 @@
       device: string, target device serial number (or None)
       extra_args: list of strings, extra arguments for dalvikvm
     """
-    self._test_env = DeviceTestEnv('javafuzz_', specific_device=device)
+    self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
     self._dalvik_cmd = ['dalvikvm']
     if extra_args is not None:
       self._dalvik_cmd += extra_args
@@ -335,14 +298,15 @@
 
 
 #
-# Tester classes.
+# Tester class.
 #
 
 
-class JavaFuzzTester(object):
-  """Tester that runs JavaFuzz many times and report divergences."""
+class JFuzzTester(object):
+  """Tester that runs JFuzz many times and report divergences."""
 
-  def  __init__(self, num_tests, device, mode1, mode2):
+  def  __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
+                report_script, true_divergence_only):
     """Constructor for the tester.
 
     Args:
@@ -350,13 +314,20 @@
       device: string, target device serial number (or None)
       mode1: string, execution mode for first runner
       mode2: string, execution mode for second runner
+      jfuzz_args: list of strings, additional arguments for jfuzz
+      report_script: string, path to script called for each divergence
+      true_divergence_only: boolean, if True don't bisect timeout divergences
     """
     self._num_tests = num_tests
     self._device = device
     self._runner1 = GetExecutionModeRunner(device, mode1)
     self._runner2 = GetExecutionModeRunner(device, mode2)
+    self._jfuzz_args = jfuzz_args
+    self._report_script = report_script
+    self._true_divergence_only = true_divergence_only
     self._save_dir = None
-    self._tmp_dir = None
+    self._results_dir = None
+    self._jfuzz_dir = None
     # Statistics.
     self._test = 0
     self._num_success = 0
@@ -373,23 +344,23 @@
     """
     self._save_dir = os.getcwd()
     self._results_dir = mkdtemp(dir='/tmp/')
-    self._tmp_dir = mkdtemp(dir=self._results_dir)
-    if self._tmp_dir is None or self._results_dir is None:
+    self._jfuzz_dir = mkdtemp(dir=self._results_dir)
+    if self._results_dir is None or self._jfuzz_dir is None:
       raise FatalError('Cannot obtain temp directory')
-    os.chdir(self._tmp_dir)
+    os.chdir(self._jfuzz_dir)
     return self
 
   def __exit__(self, etype, evalue, etraceback):
     """On exit, re-enters previously saved current directory and cleans up."""
     os.chdir(self._save_dir)
-    shutil.rmtree(self._tmp_dir)
+    shutil.rmtree(self._jfuzz_dir)
     if self._num_divergences == 0:
       shutil.rmtree(self._results_dir)
 
   def Run(self):
-    """Runs JavaFuzz many times and report divergences."""
+    """Runs JFuzz many times and report divergences."""
     print()
-    print('**\n**** JavaFuzz Testing\n**')
+    print('**\n**** JFuzz Testing\n**')
     print()
     print('#Tests    :', self._num_tests)
     print('Device    :', self._device)
@@ -399,7 +370,7 @@
     print()
     self.ShowStats()
     for self._test in range(1, self._num_tests + 1):
-      self.RunJavaFuzzTest()
+      self.RunJFuzzTest()
       self.ShowStats()
     if self._num_divergences == 0:
       print('\n\nsuccess (no divergences)\n')
@@ -408,16 +379,17 @@
 
   def ShowStats(self):
     """Shows current statistics (on same line) while tester is running."""
-    print('\rTests:', self._test, \
-          'Success:', self._num_success, \
-          'Not-compiled:', self._num_not_compiled, \
-          'Not-run:', self._num_not_run, \
-          'Timed-out:', self._num_timed_out, \
-          'Divergences:', self._num_divergences, end='')
+    print('\rTests:', self._test,
+          'Success:', self._num_success,
+          'Not-compiled:', self._num_not_compiled,
+          'Not-run:', self._num_not_run,
+          'Timed-out:', self._num_timed_out,
+          'Divergences:', self._num_divergences,
+          end='')
     sys.stdout.flush()
 
-  def RunJavaFuzzTest(self):
-    """Runs a single JavaFuzz test, comparing two execution modes."""
+  def RunJFuzzTest(self):
+    """Runs a single JFuzz test, comparing two execution modes."""
     self.ConstructTest()
     retc1 = self._runner1.CompileAndRunTest()
     retc2 = self._runner2.CompileAndRunTest()
@@ -425,13 +397,14 @@
     self.CleanupTest()
 
   def ConstructTest(self):
-    """Use JavaFuzz to generate next Test.java test.
+    """Use JFuzz to generate next Test.java test.
 
     Raises:
-      FatalError: error when javafuzz fails
+      FatalError: error when jfuzz fails
     """
-    if RunCommand(['javafuzz'], out='Test.java', err=None) != RetCode.SUCCESS:
-      raise FatalError('Unexpected error while running JavaFuzz')
+    if (RunCommand(['jfuzz'] + self._jfuzz_args, out='Test.java', err=None)
+          != RetCode.SUCCESS):
+      raise FatalError('Unexpected error while running JFuzz')
 
   def CheckForDivergence(self, retc1, retc2):
     """Checks for divergences and updates statistics.
@@ -477,9 +450,48 @@
     os.mkdir(ddir)
     for f in glob('*.txt') + ['Test.java']:
       shutil.copy(f, ddir)
-    # Maybe run bisection bug search.
-    if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
-      self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
+    if not (self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2)):
+      # Maybe run bisection bug search.
+      if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
+        self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
+      # Call reporting script.
+      if self._report_script:
+        self.RunReportScript(retc1, retc2, is_output_divergence)
+
+  def RunReportScript(self, retc1, retc2, is_output_divergence):
+    """Runs report script."""
+    try:
+      title = "Divergence between {0} and {1} (found with fuzz testing)".format(
+          self._runner1.description, self._runner2.description)
+      # Prepare divergence comment.
+      jfuzz_cmd_and_version = subprocess.check_output(
+          ['grep', '-o', 'jfuzz.*', 'Test.java'], universal_newlines=True)
+      (jfuzz_cmd_str, jfuzz_ver) = jfuzz_cmd_and_version.split('(')
+      # Strip right parenthesis and new line.
+      jfuzz_ver = jfuzz_ver[:-2]
+      jfuzz_args = ['\'-{0}\''.format(arg)
+                    for arg in jfuzz_cmd_str.strip().split(' -')][1:]
+      wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
+      repro_cmd_str = (os.path.basename(__file__) + ' --num_tests 1 ' +
+                       ' '.join(wrapped_args))
+      comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
+          jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)
+      if is_output_divergence:
+        (output, _, _) = RunCommandForOutput(
+            ['diff', self._runner1.output_file, self._runner2.output_file],
+            None, subprocess.PIPE, subprocess.STDOUT)
+        comment += 'Diff:\n' + output
+      else:
+        comment += '{0} vs {1}\n'.format(retc1, retc2)
+      # Prepare report script command.
+      script_cmd = [self._report_script, title, comment]
+      ddir = self.GetCurrentDivergenceDir()
+      bisection_out_files = glob(ddir + '/*_bisection_out.txt')
+      if bisection_out_files:
+        script_cmd += ['--bisection_out', bisection_out_files[0]]
+      subprocess.check_call(script_cmd, stdout=DEVNULL, stderr=DEVNULL)
+    except subprocess.CalledProcessError as err:
+      print('Failed to run report script.\n', err)
 
   def RunBisectionSearch(self, args, expected_retcode, expected_output,
                          runner_id):
@@ -515,12 +527,12 @@
 
   def CleanupTest(self):
     """Cleans up after a single test run."""
-    for file_name in os.listdir(self._tmp_dir):
-        file_path = os.path.join(self._tmp_dir, file_name)
-        if os.path.isfile(file_path):
-          os.unlink(file_path)
-        elif os.path.isdir(file_path):
-          shutil.rmtree(file_path)
+    for file_name in os.listdir(self._jfuzz_dir):
+      file_path = os.path.join(self._jfuzz_dir, file_name)
+      if os.path.isfile(file_path):
+        os.unlink(file_path)
+      elif os.path.isdir(file_path):
+        shutil.rmtree(file_path)
 
 
 def main():
@@ -533,12 +545,19 @@
                       help='execution mode 1 (default: ri)')
   parser.add_argument('--mode2', default='hopt',
                       help='execution mode 2 (default: hopt)')
+  parser.add_argument('--report_script', help='script called for each'
+                                              'divergence')
+  parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
+                      action='append', help='argument for jfuzz')
+  parser.add_argument('--true_divergence', default=False, action='store_true',
+                      help='don\'t bisect timeout divergences')
   args = parser.parse_args()
   if args.mode1 == args.mode2:
     raise FatalError('Identical execution modes given')
-  # Run the JavaFuzz tester.
-  with JavaFuzzTester(args.num_tests, args.device,
-                      args.mode1, args.mode2) as fuzzer:
+  # Run the JFuzz tester.
+  with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2,
+                   args.jfuzz_args, args.report_script,
+                   args.true_divergence) as fuzzer:
     fuzzer.Run()
 
 if __name__ == '__main__':
diff --git a/tools/jfuzz/run_jfuzz_test_nightly.py b/tools/jfuzz/run_jfuzz_test_nightly.py
new file mode 100755
index 0000000..29595f2
--- /dev/null
+++ b/tools/jfuzz/run_jfuzz_test_nightly.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+from glob import glob
+
+from tempfile import mkdtemp
+from tempfile import TemporaryFile
+
+# Default arguments for run_jfuzz_test.py.
+DEFAULT_ARGS = ['--num_tests=20000']
+
+# run_jfuzz_test.py success string.
+SUCCESS_STRING = 'success (no divergences)'
+
+# Constant returned by string find() method when search fails.
+NOT_FOUND = -1
+
+def main(argv):
+  cwd = os.path.dirname(os.path.realpath(__file__))
+  cmd = [cwd + '/run_jfuzz_test.py'] + DEFAULT_ARGS
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--num_proc', default=8,
+                      type=int, help='number of processes to run')
+  # Unknown arguments are passed to run_jfuzz_test.py.
+  (args, unknown_args) = parser.parse_known_args()
+  output_files = [TemporaryFile('wb+') for _ in range(args.num_proc)]
+  processes = []
+  for output_file in output_files:
+    processes.append(subprocess.Popen(cmd + unknown_args, stdout=output_file,
+                                      stderr=subprocess.STDOUT))
+  try:
+    # Wait for processes to terminate.
+    for proc in processes:
+      proc.wait()
+  except KeyboardInterrupt:
+    for proc in processes:
+      proc.kill()
+  # Output results.
+  output_dirs = []
+  for i, output_file in enumerate(output_files):
+    output_file.seek(0)
+    output_str = output_file.read().decode('ascii')
+    output_file.close()
+    # Extract output directory. Example match: 'Directory : /tmp/tmp8ltpfjng'.
+    directory_match = re.search(r'Directory[^:]*: ([^\n]+)\n', output_str)
+    if directory_match:
+      output_dirs.append(directory_match.group(1))
+    print('Tester', i)
+    if output_str.find(SUCCESS_STRING) == NOT_FOUND:
+      print(output_str)
+    else:
+      print(SUCCESS_STRING)
+  # Gather divergences.
+  global_out_dir = mkdtemp('jfuzz_nightly')
+  divergence_nr = 1
+  for out_dir in output_dirs:
+    for divergence_dir in glob(out_dir + '/divergence*/'):
+      shutil.copytree(divergence_dir,
+                      global_out_dir + '/divergence' + str(divergence_nr))
+      divergence_nr += 1
+  print('Global output directory:', global_out_dir)
+
+if __name__ == '__main__':
+  main(sys.argv)
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index 95f0c2d..0e289a6 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -10,11 +10,4 @@
  */
 
 [
-{
-  description: "Assertion failing on the concurrent collector configuration.",
-  result: EXEC_FAILED,
-  names: ["jsr166.LinkedTransferQueueTest#testTransfer2",
-          "jsr166.LinkedTransferQueueTest#testWaitingConsumer"],
-  bug: 25883050
-}
 ]