Merge "X86_64: Add allocation entrypoint switching for CC is_marking"
diff --git a/benchmark/const-class/info.txt b/benchmark/const-class/info.txt
new file mode 100644
index 0000000..ed0b827
--- /dev/null
+++ b/benchmark/const-class/info.txt
@@ -0,0 +1 @@
+Benchmarks for repeating const-class instructions in a loop.
diff --git a/benchmark/const-class/src/ConstClassBenchmark.java b/benchmark/const-class/src/ConstClassBenchmark.java
new file mode 100644
index 0000000..d45b49f
--- /dev/null
+++ b/benchmark/const-class/src/ConstClassBenchmark.java
@@ -0,0 +1,1071 @@
+/*
+ * 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 ConstClassBenchmark {
+    // Define 1025 classes with consecutive type indexes in the dex file.
+    // The tests below rely on the knowledge that ART uses the low 10 bits
+    // of the type index as the hash into DexCache types array.
+    // Note: n == n + 1024 (mod 2^10), n + 1 != n + 1023 (mod 2^10).
+    public static class TestClass_0000 {}
+    public static class TestClass_0001 {}
+    public static class TestClass_0002 {}
+    public static class TestClass_0003 {}
+    public static class TestClass_0004 {}
+    public static class TestClass_0005 {}
+    public static class TestClass_0006 {}
+    public static class TestClass_0007 {}
+    public static class TestClass_0008 {}
+    public static class TestClass_0009 {}
+    public static class TestClass_0010 {}
+    public static class TestClass_0011 {}
+    public static class TestClass_0012 {}
+    public static class TestClass_0013 {}
+    public static class TestClass_0014 {}
+    public static class TestClass_0015 {}
+    public static class TestClass_0016 {}
+    public static class TestClass_0017 {}
+    public static class TestClass_0018 {}
+    public static class TestClass_0019 {}
+    public static class TestClass_0020 {}
+    public static class TestClass_0021 {}
+    public static class TestClass_0022 {}
+    public static class TestClass_0023 {}
+    public static class TestClass_0024 {}
+    public static class TestClass_0025 {}
+    public static class TestClass_0026 {}
+    public static class TestClass_0027 {}
+    public static class TestClass_0028 {}
+    public static class TestClass_0029 {}
+    public static class TestClass_0030 {}
+    public static class TestClass_0031 {}
+    public static class TestClass_0032 {}
+    public static class TestClass_0033 {}
+    public static class TestClass_0034 {}
+    public static class TestClass_0035 {}
+    public static class TestClass_0036 {}
+    public static class TestClass_0037 {}
+    public static class TestClass_0038 {}
+    public static class TestClass_0039 {}
+    public static class TestClass_0040 {}
+    public static class TestClass_0041 {}
+    public static class TestClass_0042 {}
+    public static class TestClass_0043 {}
+    public static class TestClass_0044 {}
+    public static class TestClass_0045 {}
+    public static class TestClass_0046 {}
+    public static class TestClass_0047 {}
+    public static class TestClass_0048 {}
+    public static class TestClass_0049 {}
+    public static class TestClass_0050 {}
+    public static class TestClass_0051 {}
+    public static class TestClass_0052 {}
+    public static class TestClass_0053 {}
+    public static class TestClass_0054 {}
+    public static class TestClass_0055 {}
+    public static class TestClass_0056 {}
+    public static class TestClass_0057 {}
+    public static class TestClass_0058 {}
+    public static class TestClass_0059 {}
+    public static class TestClass_0060 {}
+    public static class TestClass_0061 {}
+    public static class TestClass_0062 {}
+    public static class TestClass_0063 {}
+    public static class TestClass_0064 {}
+    public static class TestClass_0065 {}
+    public static class TestClass_0066 {}
+    public static class TestClass_0067 {}
+    public static class TestClass_0068 {}
+    public static class TestClass_0069 {}
+    public static class TestClass_0070 {}
+    public static class TestClass_0071 {}
+    public static class TestClass_0072 {}
+    public static class TestClass_0073 {}
+    public static class TestClass_0074 {}
+    public static class TestClass_0075 {}
+    public static class TestClass_0076 {}
+    public static class TestClass_0077 {}
+    public static class TestClass_0078 {}
+    public static class TestClass_0079 {}
+    public static class TestClass_0080 {}
+    public static class TestClass_0081 {}
+    public static class TestClass_0082 {}
+    public static class TestClass_0083 {}
+    public static class TestClass_0084 {}
+    public static class TestClass_0085 {}
+    public static class TestClass_0086 {}
+    public static class TestClass_0087 {}
+    public static class TestClass_0088 {}
+    public static class TestClass_0089 {}
+    public static class TestClass_0090 {}
+    public static class TestClass_0091 {}
+    public static class TestClass_0092 {}
+    public static class TestClass_0093 {}
+    public static class TestClass_0094 {}
+    public static class TestClass_0095 {}
+    public static class TestClass_0096 {}
+    public static class TestClass_0097 {}
+    public static class TestClass_0098 {}
+    public static class TestClass_0099 {}
+    public static class TestClass_0100 {}
+    public static class TestClass_0101 {}
+    public static class TestClass_0102 {}
+    public static class TestClass_0103 {}
+    public static class TestClass_0104 {}
+    public static class TestClass_0105 {}
+    public static class TestClass_0106 {}
+    public static class TestClass_0107 {}
+    public static class TestClass_0108 {}
+    public static class TestClass_0109 {}
+    public static class TestClass_0110 {}
+    public static class TestClass_0111 {}
+    public static class TestClass_0112 {}
+    public static class TestClass_0113 {}
+    public static class TestClass_0114 {}
+    public static class TestClass_0115 {}
+    public static class TestClass_0116 {}
+    public static class TestClass_0117 {}
+    public static class TestClass_0118 {}
+    public static class TestClass_0119 {}
+    public static class TestClass_0120 {}
+    public static class TestClass_0121 {}
+    public static class TestClass_0122 {}
+    public static class TestClass_0123 {}
+    public static class TestClass_0124 {}
+    public static class TestClass_0125 {}
+    public static class TestClass_0126 {}
+    public static class TestClass_0127 {}
+    public static class TestClass_0128 {}
+    public static class TestClass_0129 {}
+    public static class TestClass_0130 {}
+    public static class TestClass_0131 {}
+    public static class TestClass_0132 {}
+    public static class TestClass_0133 {}
+    public static class TestClass_0134 {}
+    public static class TestClass_0135 {}
+    public static class TestClass_0136 {}
+    public static class TestClass_0137 {}
+    public static class TestClass_0138 {}
+    public static class TestClass_0139 {}
+    public static class TestClass_0140 {}
+    public static class TestClass_0141 {}
+    public static class TestClass_0142 {}
+    public static class TestClass_0143 {}
+    public static class TestClass_0144 {}
+    public static class TestClass_0145 {}
+    public static class TestClass_0146 {}
+    public static class TestClass_0147 {}
+    public static class TestClass_0148 {}
+    public static class TestClass_0149 {}
+    public static class TestClass_0150 {}
+    public static class TestClass_0151 {}
+    public static class TestClass_0152 {}
+    public static class TestClass_0153 {}
+    public static class TestClass_0154 {}
+    public static class TestClass_0155 {}
+    public static class TestClass_0156 {}
+    public static class TestClass_0157 {}
+    public static class TestClass_0158 {}
+    public static class TestClass_0159 {}
+    public static class TestClass_0160 {}
+    public static class TestClass_0161 {}
+    public static class TestClass_0162 {}
+    public static class TestClass_0163 {}
+    public static class TestClass_0164 {}
+    public static class TestClass_0165 {}
+    public static class TestClass_0166 {}
+    public static class TestClass_0167 {}
+    public static class TestClass_0168 {}
+    public static class TestClass_0169 {}
+    public static class TestClass_0170 {}
+    public static class TestClass_0171 {}
+    public static class TestClass_0172 {}
+    public static class TestClass_0173 {}
+    public static class TestClass_0174 {}
+    public static class TestClass_0175 {}
+    public static class TestClass_0176 {}
+    public static class TestClass_0177 {}
+    public static class TestClass_0178 {}
+    public static class TestClass_0179 {}
+    public static class TestClass_0180 {}
+    public static class TestClass_0181 {}
+    public static class TestClass_0182 {}
+    public static class TestClass_0183 {}
+    public static class TestClass_0184 {}
+    public static class TestClass_0185 {}
+    public static class TestClass_0186 {}
+    public static class TestClass_0187 {}
+    public static class TestClass_0188 {}
+    public static class TestClass_0189 {}
+    public static class TestClass_0190 {}
+    public static class TestClass_0191 {}
+    public static class TestClass_0192 {}
+    public static class TestClass_0193 {}
+    public static class TestClass_0194 {}
+    public static class TestClass_0195 {}
+    public static class TestClass_0196 {}
+    public static class TestClass_0197 {}
+    public static class TestClass_0198 {}
+    public static class TestClass_0199 {}
+    public static class TestClass_0200 {}
+    public static class TestClass_0201 {}
+    public static class TestClass_0202 {}
+    public static class TestClass_0203 {}
+    public static class TestClass_0204 {}
+    public static class TestClass_0205 {}
+    public static class TestClass_0206 {}
+    public static class TestClass_0207 {}
+    public static class TestClass_0208 {}
+    public static class TestClass_0209 {}
+    public static class TestClass_0210 {}
+    public static class TestClass_0211 {}
+    public static class TestClass_0212 {}
+    public static class TestClass_0213 {}
+    public static class TestClass_0214 {}
+    public static class TestClass_0215 {}
+    public static class TestClass_0216 {}
+    public static class TestClass_0217 {}
+    public static class TestClass_0218 {}
+    public static class TestClass_0219 {}
+    public static class TestClass_0220 {}
+    public static class TestClass_0221 {}
+    public static class TestClass_0222 {}
+    public static class TestClass_0223 {}
+    public static class TestClass_0224 {}
+    public static class TestClass_0225 {}
+    public static class TestClass_0226 {}
+    public static class TestClass_0227 {}
+    public static class TestClass_0228 {}
+    public static class TestClass_0229 {}
+    public static class TestClass_0230 {}
+    public static class TestClass_0231 {}
+    public static class TestClass_0232 {}
+    public static class TestClass_0233 {}
+    public static class TestClass_0234 {}
+    public static class TestClass_0235 {}
+    public static class TestClass_0236 {}
+    public static class TestClass_0237 {}
+    public static class TestClass_0238 {}
+    public static class TestClass_0239 {}
+    public static class TestClass_0240 {}
+    public static class TestClass_0241 {}
+    public static class TestClass_0242 {}
+    public static class TestClass_0243 {}
+    public static class TestClass_0244 {}
+    public static class TestClass_0245 {}
+    public static class TestClass_0246 {}
+    public static class TestClass_0247 {}
+    public static class TestClass_0248 {}
+    public static class TestClass_0249 {}
+    public static class TestClass_0250 {}
+    public static class TestClass_0251 {}
+    public static class TestClass_0252 {}
+    public static class TestClass_0253 {}
+    public static class TestClass_0254 {}
+    public static class TestClass_0255 {}
+    public static class TestClass_0256 {}
+    public static class TestClass_0257 {}
+    public static class TestClass_0258 {}
+    public static class TestClass_0259 {}
+    public static class TestClass_0260 {}
+    public static class TestClass_0261 {}
+    public static class TestClass_0262 {}
+    public static class TestClass_0263 {}
+    public static class TestClass_0264 {}
+    public static class TestClass_0265 {}
+    public static class TestClass_0266 {}
+    public static class TestClass_0267 {}
+    public static class TestClass_0268 {}
+    public static class TestClass_0269 {}
+    public static class TestClass_0270 {}
+    public static class TestClass_0271 {}
+    public static class TestClass_0272 {}
+    public static class TestClass_0273 {}
+    public static class TestClass_0274 {}
+    public static class TestClass_0275 {}
+    public static class TestClass_0276 {}
+    public static class TestClass_0277 {}
+    public static class TestClass_0278 {}
+    public static class TestClass_0279 {}
+    public static class TestClass_0280 {}
+    public static class TestClass_0281 {}
+    public static class TestClass_0282 {}
+    public static class TestClass_0283 {}
+    public static class TestClass_0284 {}
+    public static class TestClass_0285 {}
+    public static class TestClass_0286 {}
+    public static class TestClass_0287 {}
+    public static class TestClass_0288 {}
+    public static class TestClass_0289 {}
+    public static class TestClass_0290 {}
+    public static class TestClass_0291 {}
+    public static class TestClass_0292 {}
+    public static class TestClass_0293 {}
+    public static class TestClass_0294 {}
+    public static class TestClass_0295 {}
+    public static class TestClass_0296 {}
+    public static class TestClass_0297 {}
+    public static class TestClass_0298 {}
+    public static class TestClass_0299 {}
+    public static class TestClass_0300 {}
+    public static class TestClass_0301 {}
+    public static class TestClass_0302 {}
+    public static class TestClass_0303 {}
+    public static class TestClass_0304 {}
+    public static class TestClass_0305 {}
+    public static class TestClass_0306 {}
+    public static class TestClass_0307 {}
+    public static class TestClass_0308 {}
+    public static class TestClass_0309 {}
+    public static class TestClass_0310 {}
+    public static class TestClass_0311 {}
+    public static class TestClass_0312 {}
+    public static class TestClass_0313 {}
+    public static class TestClass_0314 {}
+    public static class TestClass_0315 {}
+    public static class TestClass_0316 {}
+    public static class TestClass_0317 {}
+    public static class TestClass_0318 {}
+    public static class TestClass_0319 {}
+    public static class TestClass_0320 {}
+    public static class TestClass_0321 {}
+    public static class TestClass_0322 {}
+    public static class TestClass_0323 {}
+    public static class TestClass_0324 {}
+    public static class TestClass_0325 {}
+    public static class TestClass_0326 {}
+    public static class TestClass_0327 {}
+    public static class TestClass_0328 {}
+    public static class TestClass_0329 {}
+    public static class TestClass_0330 {}
+    public static class TestClass_0331 {}
+    public static class TestClass_0332 {}
+    public static class TestClass_0333 {}
+    public static class TestClass_0334 {}
+    public static class TestClass_0335 {}
+    public static class TestClass_0336 {}
+    public static class TestClass_0337 {}
+    public static class TestClass_0338 {}
+    public static class TestClass_0339 {}
+    public static class TestClass_0340 {}
+    public static class TestClass_0341 {}
+    public static class TestClass_0342 {}
+    public static class TestClass_0343 {}
+    public static class TestClass_0344 {}
+    public static class TestClass_0345 {}
+    public static class TestClass_0346 {}
+    public static class TestClass_0347 {}
+    public static class TestClass_0348 {}
+    public static class TestClass_0349 {}
+    public static class TestClass_0350 {}
+    public static class TestClass_0351 {}
+    public static class TestClass_0352 {}
+    public static class TestClass_0353 {}
+    public static class TestClass_0354 {}
+    public static class TestClass_0355 {}
+    public static class TestClass_0356 {}
+    public static class TestClass_0357 {}
+    public static class TestClass_0358 {}
+    public static class TestClass_0359 {}
+    public static class TestClass_0360 {}
+    public static class TestClass_0361 {}
+    public static class TestClass_0362 {}
+    public static class TestClass_0363 {}
+    public static class TestClass_0364 {}
+    public static class TestClass_0365 {}
+    public static class TestClass_0366 {}
+    public static class TestClass_0367 {}
+    public static class TestClass_0368 {}
+    public static class TestClass_0369 {}
+    public static class TestClass_0370 {}
+    public static class TestClass_0371 {}
+    public static class TestClass_0372 {}
+    public static class TestClass_0373 {}
+    public static class TestClass_0374 {}
+    public static class TestClass_0375 {}
+    public static class TestClass_0376 {}
+    public static class TestClass_0377 {}
+    public static class TestClass_0378 {}
+    public static class TestClass_0379 {}
+    public static class TestClass_0380 {}
+    public static class TestClass_0381 {}
+    public static class TestClass_0382 {}
+    public static class TestClass_0383 {}
+    public static class TestClass_0384 {}
+    public static class TestClass_0385 {}
+    public static class TestClass_0386 {}
+    public static class TestClass_0387 {}
+    public static class TestClass_0388 {}
+    public static class TestClass_0389 {}
+    public static class TestClass_0390 {}
+    public static class TestClass_0391 {}
+    public static class TestClass_0392 {}
+    public static class TestClass_0393 {}
+    public static class TestClass_0394 {}
+    public static class TestClass_0395 {}
+    public static class TestClass_0396 {}
+    public static class TestClass_0397 {}
+    public static class TestClass_0398 {}
+    public static class TestClass_0399 {}
+    public static class TestClass_0400 {}
+    public static class TestClass_0401 {}
+    public static class TestClass_0402 {}
+    public static class TestClass_0403 {}
+    public static class TestClass_0404 {}
+    public static class TestClass_0405 {}
+    public static class TestClass_0406 {}
+    public static class TestClass_0407 {}
+    public static class TestClass_0408 {}
+    public static class TestClass_0409 {}
+    public static class TestClass_0410 {}
+    public static class TestClass_0411 {}
+    public static class TestClass_0412 {}
+    public static class TestClass_0413 {}
+    public static class TestClass_0414 {}
+    public static class TestClass_0415 {}
+    public static class TestClass_0416 {}
+    public static class TestClass_0417 {}
+    public static class TestClass_0418 {}
+    public static class TestClass_0419 {}
+    public static class TestClass_0420 {}
+    public static class TestClass_0421 {}
+    public static class TestClass_0422 {}
+    public static class TestClass_0423 {}
+    public static class TestClass_0424 {}
+    public static class TestClass_0425 {}
+    public static class TestClass_0426 {}
+    public static class TestClass_0427 {}
+    public static class TestClass_0428 {}
+    public static class TestClass_0429 {}
+    public static class TestClass_0430 {}
+    public static class TestClass_0431 {}
+    public static class TestClass_0432 {}
+    public static class TestClass_0433 {}
+    public static class TestClass_0434 {}
+    public static class TestClass_0435 {}
+    public static class TestClass_0436 {}
+    public static class TestClass_0437 {}
+    public static class TestClass_0438 {}
+    public static class TestClass_0439 {}
+    public static class TestClass_0440 {}
+    public static class TestClass_0441 {}
+    public static class TestClass_0442 {}
+    public static class TestClass_0443 {}
+    public static class TestClass_0444 {}
+    public static class TestClass_0445 {}
+    public static class TestClass_0446 {}
+    public static class TestClass_0447 {}
+    public static class TestClass_0448 {}
+    public static class TestClass_0449 {}
+    public static class TestClass_0450 {}
+    public static class TestClass_0451 {}
+    public static class TestClass_0452 {}
+    public static class TestClass_0453 {}
+    public static class TestClass_0454 {}
+    public static class TestClass_0455 {}
+    public static class TestClass_0456 {}
+    public static class TestClass_0457 {}
+    public static class TestClass_0458 {}
+    public static class TestClass_0459 {}
+    public static class TestClass_0460 {}
+    public static class TestClass_0461 {}
+    public static class TestClass_0462 {}
+    public static class TestClass_0463 {}
+    public static class TestClass_0464 {}
+    public static class TestClass_0465 {}
+    public static class TestClass_0466 {}
+    public static class TestClass_0467 {}
+    public static class TestClass_0468 {}
+    public static class TestClass_0469 {}
+    public static class TestClass_0470 {}
+    public static class TestClass_0471 {}
+    public static class TestClass_0472 {}
+    public static class TestClass_0473 {}
+    public static class TestClass_0474 {}
+    public static class TestClass_0475 {}
+    public static class TestClass_0476 {}
+    public static class TestClass_0477 {}
+    public static class TestClass_0478 {}
+    public static class TestClass_0479 {}
+    public static class TestClass_0480 {}
+    public static class TestClass_0481 {}
+    public static class TestClass_0482 {}
+    public static class TestClass_0483 {}
+    public static class TestClass_0484 {}
+    public static class TestClass_0485 {}
+    public static class TestClass_0486 {}
+    public static class TestClass_0487 {}
+    public static class TestClass_0488 {}
+    public static class TestClass_0489 {}
+    public static class TestClass_0490 {}
+    public static class TestClass_0491 {}
+    public static class TestClass_0492 {}
+    public static class TestClass_0493 {}
+    public static class TestClass_0494 {}
+    public static class TestClass_0495 {}
+    public static class TestClass_0496 {}
+    public static class TestClass_0497 {}
+    public static class TestClass_0498 {}
+    public static class TestClass_0499 {}
+    public static class TestClass_0500 {}
+    public static class TestClass_0501 {}
+    public static class TestClass_0502 {}
+    public static class TestClass_0503 {}
+    public static class TestClass_0504 {}
+    public static class TestClass_0505 {}
+    public static class TestClass_0506 {}
+    public static class TestClass_0507 {}
+    public static class TestClass_0508 {}
+    public static class TestClass_0509 {}
+    public static class TestClass_0510 {}
+    public static class TestClass_0511 {}
+    public static class TestClass_0512 {}
+    public static class TestClass_0513 {}
+    public static class TestClass_0514 {}
+    public static class TestClass_0515 {}
+    public static class TestClass_0516 {}
+    public static class TestClass_0517 {}
+    public static class TestClass_0518 {}
+    public static class TestClass_0519 {}
+    public static class TestClass_0520 {}
+    public static class TestClass_0521 {}
+    public static class TestClass_0522 {}
+    public static class TestClass_0523 {}
+    public static class TestClass_0524 {}
+    public static class TestClass_0525 {}
+    public static class TestClass_0526 {}
+    public static class TestClass_0527 {}
+    public static class TestClass_0528 {}
+    public static class TestClass_0529 {}
+    public static class TestClass_0530 {}
+    public static class TestClass_0531 {}
+    public static class TestClass_0532 {}
+    public static class TestClass_0533 {}
+    public static class TestClass_0534 {}
+    public static class TestClass_0535 {}
+    public static class TestClass_0536 {}
+    public static class TestClass_0537 {}
+    public static class TestClass_0538 {}
+    public static class TestClass_0539 {}
+    public static class TestClass_0540 {}
+    public static class TestClass_0541 {}
+    public static class TestClass_0542 {}
+    public static class TestClass_0543 {}
+    public static class TestClass_0544 {}
+    public static class TestClass_0545 {}
+    public static class TestClass_0546 {}
+    public static class TestClass_0547 {}
+    public static class TestClass_0548 {}
+    public static class TestClass_0549 {}
+    public static class TestClass_0550 {}
+    public static class TestClass_0551 {}
+    public static class TestClass_0552 {}
+    public static class TestClass_0553 {}
+    public static class TestClass_0554 {}
+    public static class TestClass_0555 {}
+    public static class TestClass_0556 {}
+    public static class TestClass_0557 {}
+    public static class TestClass_0558 {}
+    public static class TestClass_0559 {}
+    public static class TestClass_0560 {}
+    public static class TestClass_0561 {}
+    public static class TestClass_0562 {}
+    public static class TestClass_0563 {}
+    public static class TestClass_0564 {}
+    public static class TestClass_0565 {}
+    public static class TestClass_0566 {}
+    public static class TestClass_0567 {}
+    public static class TestClass_0568 {}
+    public static class TestClass_0569 {}
+    public static class TestClass_0570 {}
+    public static class TestClass_0571 {}
+    public static class TestClass_0572 {}
+    public static class TestClass_0573 {}
+    public static class TestClass_0574 {}
+    public static class TestClass_0575 {}
+    public static class TestClass_0576 {}
+    public static class TestClass_0577 {}
+    public static class TestClass_0578 {}
+    public static class TestClass_0579 {}
+    public static class TestClass_0580 {}
+    public static class TestClass_0581 {}
+    public static class TestClass_0582 {}
+    public static class TestClass_0583 {}
+    public static class TestClass_0584 {}
+    public static class TestClass_0585 {}
+    public static class TestClass_0586 {}
+    public static class TestClass_0587 {}
+    public static class TestClass_0588 {}
+    public static class TestClass_0589 {}
+    public static class TestClass_0590 {}
+    public static class TestClass_0591 {}
+    public static class TestClass_0592 {}
+    public static class TestClass_0593 {}
+    public static class TestClass_0594 {}
+    public static class TestClass_0595 {}
+    public static class TestClass_0596 {}
+    public static class TestClass_0597 {}
+    public static class TestClass_0598 {}
+    public static class TestClass_0599 {}
+    public static class TestClass_0600 {}
+    public static class TestClass_0601 {}
+    public static class TestClass_0602 {}
+    public static class TestClass_0603 {}
+    public static class TestClass_0604 {}
+    public static class TestClass_0605 {}
+    public static class TestClass_0606 {}
+    public static class TestClass_0607 {}
+    public static class TestClass_0608 {}
+    public static class TestClass_0609 {}
+    public static class TestClass_0610 {}
+    public static class TestClass_0611 {}
+    public static class TestClass_0612 {}
+    public static class TestClass_0613 {}
+    public static class TestClass_0614 {}
+    public static class TestClass_0615 {}
+    public static class TestClass_0616 {}
+    public static class TestClass_0617 {}
+    public static class TestClass_0618 {}
+    public static class TestClass_0619 {}
+    public static class TestClass_0620 {}
+    public static class TestClass_0621 {}
+    public static class TestClass_0622 {}
+    public static class TestClass_0623 {}
+    public static class TestClass_0624 {}
+    public static class TestClass_0625 {}
+    public static class TestClass_0626 {}
+    public static class TestClass_0627 {}
+    public static class TestClass_0628 {}
+    public static class TestClass_0629 {}
+    public static class TestClass_0630 {}
+    public static class TestClass_0631 {}
+    public static class TestClass_0632 {}
+    public static class TestClass_0633 {}
+    public static class TestClass_0634 {}
+    public static class TestClass_0635 {}
+    public static class TestClass_0636 {}
+    public static class TestClass_0637 {}
+    public static class TestClass_0638 {}
+    public static class TestClass_0639 {}
+    public static class TestClass_0640 {}
+    public static class TestClass_0641 {}
+    public static class TestClass_0642 {}
+    public static class TestClass_0643 {}
+    public static class TestClass_0644 {}
+    public static class TestClass_0645 {}
+    public static class TestClass_0646 {}
+    public static class TestClass_0647 {}
+    public static class TestClass_0648 {}
+    public static class TestClass_0649 {}
+    public static class TestClass_0650 {}
+    public static class TestClass_0651 {}
+    public static class TestClass_0652 {}
+    public static class TestClass_0653 {}
+    public static class TestClass_0654 {}
+    public static class TestClass_0655 {}
+    public static class TestClass_0656 {}
+    public static class TestClass_0657 {}
+    public static class TestClass_0658 {}
+    public static class TestClass_0659 {}
+    public static class TestClass_0660 {}
+    public static class TestClass_0661 {}
+    public static class TestClass_0662 {}
+    public static class TestClass_0663 {}
+    public static class TestClass_0664 {}
+    public static class TestClass_0665 {}
+    public static class TestClass_0666 {}
+    public static class TestClass_0667 {}
+    public static class TestClass_0668 {}
+    public static class TestClass_0669 {}
+    public static class TestClass_0670 {}
+    public static class TestClass_0671 {}
+    public static class TestClass_0672 {}
+    public static class TestClass_0673 {}
+    public static class TestClass_0674 {}
+    public static class TestClass_0675 {}
+    public static class TestClass_0676 {}
+    public static class TestClass_0677 {}
+    public static class TestClass_0678 {}
+    public static class TestClass_0679 {}
+    public static class TestClass_0680 {}
+    public static class TestClass_0681 {}
+    public static class TestClass_0682 {}
+    public static class TestClass_0683 {}
+    public static class TestClass_0684 {}
+    public static class TestClass_0685 {}
+    public static class TestClass_0686 {}
+    public static class TestClass_0687 {}
+    public static class TestClass_0688 {}
+    public static class TestClass_0689 {}
+    public static class TestClass_0690 {}
+    public static class TestClass_0691 {}
+    public static class TestClass_0692 {}
+    public static class TestClass_0693 {}
+    public static class TestClass_0694 {}
+    public static class TestClass_0695 {}
+    public static class TestClass_0696 {}
+    public static class TestClass_0697 {}
+    public static class TestClass_0698 {}
+    public static class TestClass_0699 {}
+    public static class TestClass_0700 {}
+    public static class TestClass_0701 {}
+    public static class TestClass_0702 {}
+    public static class TestClass_0703 {}
+    public static class TestClass_0704 {}
+    public static class TestClass_0705 {}
+    public static class TestClass_0706 {}
+    public static class TestClass_0707 {}
+    public static class TestClass_0708 {}
+    public static class TestClass_0709 {}
+    public static class TestClass_0710 {}
+    public static class TestClass_0711 {}
+    public static class TestClass_0712 {}
+    public static class TestClass_0713 {}
+    public static class TestClass_0714 {}
+    public static class TestClass_0715 {}
+    public static class TestClass_0716 {}
+    public static class TestClass_0717 {}
+    public static class TestClass_0718 {}
+    public static class TestClass_0719 {}
+    public static class TestClass_0720 {}
+    public static class TestClass_0721 {}
+    public static class TestClass_0722 {}
+    public static class TestClass_0723 {}
+    public static class TestClass_0724 {}
+    public static class TestClass_0725 {}
+    public static class TestClass_0726 {}
+    public static class TestClass_0727 {}
+    public static class TestClass_0728 {}
+    public static class TestClass_0729 {}
+    public static class TestClass_0730 {}
+    public static class TestClass_0731 {}
+    public static class TestClass_0732 {}
+    public static class TestClass_0733 {}
+    public static class TestClass_0734 {}
+    public static class TestClass_0735 {}
+    public static class TestClass_0736 {}
+    public static class TestClass_0737 {}
+    public static class TestClass_0738 {}
+    public static class TestClass_0739 {}
+    public static class TestClass_0740 {}
+    public static class TestClass_0741 {}
+    public static class TestClass_0742 {}
+    public static class TestClass_0743 {}
+    public static class TestClass_0744 {}
+    public static class TestClass_0745 {}
+    public static class TestClass_0746 {}
+    public static class TestClass_0747 {}
+    public static class TestClass_0748 {}
+    public static class TestClass_0749 {}
+    public static class TestClass_0750 {}
+    public static class TestClass_0751 {}
+    public static class TestClass_0752 {}
+    public static class TestClass_0753 {}
+    public static class TestClass_0754 {}
+    public static class TestClass_0755 {}
+    public static class TestClass_0756 {}
+    public static class TestClass_0757 {}
+    public static class TestClass_0758 {}
+    public static class TestClass_0759 {}
+    public static class TestClass_0760 {}
+    public static class TestClass_0761 {}
+    public static class TestClass_0762 {}
+    public static class TestClass_0763 {}
+    public static class TestClass_0764 {}
+    public static class TestClass_0765 {}
+    public static class TestClass_0766 {}
+    public static class TestClass_0767 {}
+    public static class TestClass_0768 {}
+    public static class TestClass_0769 {}
+    public static class TestClass_0770 {}
+    public static class TestClass_0771 {}
+    public static class TestClass_0772 {}
+    public static class TestClass_0773 {}
+    public static class TestClass_0774 {}
+    public static class TestClass_0775 {}
+    public static class TestClass_0776 {}
+    public static class TestClass_0777 {}
+    public static class TestClass_0778 {}
+    public static class TestClass_0779 {}
+    public static class TestClass_0780 {}
+    public static class TestClass_0781 {}
+    public static class TestClass_0782 {}
+    public static class TestClass_0783 {}
+    public static class TestClass_0784 {}
+    public static class TestClass_0785 {}
+    public static class TestClass_0786 {}
+    public static class TestClass_0787 {}
+    public static class TestClass_0788 {}
+    public static class TestClass_0789 {}
+    public static class TestClass_0790 {}
+    public static class TestClass_0791 {}
+    public static class TestClass_0792 {}
+    public static class TestClass_0793 {}
+    public static class TestClass_0794 {}
+    public static class TestClass_0795 {}
+    public static class TestClass_0796 {}
+    public static class TestClass_0797 {}
+    public static class TestClass_0798 {}
+    public static class TestClass_0799 {}
+    public static class TestClass_0800 {}
+    public static class TestClass_0801 {}
+    public static class TestClass_0802 {}
+    public static class TestClass_0803 {}
+    public static class TestClass_0804 {}
+    public static class TestClass_0805 {}
+    public static class TestClass_0806 {}
+    public static class TestClass_0807 {}
+    public static class TestClass_0808 {}
+    public static class TestClass_0809 {}
+    public static class TestClass_0810 {}
+    public static class TestClass_0811 {}
+    public static class TestClass_0812 {}
+    public static class TestClass_0813 {}
+    public static class TestClass_0814 {}
+    public static class TestClass_0815 {}
+    public static class TestClass_0816 {}
+    public static class TestClass_0817 {}
+    public static class TestClass_0818 {}
+    public static class TestClass_0819 {}
+    public static class TestClass_0820 {}
+    public static class TestClass_0821 {}
+    public static class TestClass_0822 {}
+    public static class TestClass_0823 {}
+    public static class TestClass_0824 {}
+    public static class TestClass_0825 {}
+    public static class TestClass_0826 {}
+    public static class TestClass_0827 {}
+    public static class TestClass_0828 {}
+    public static class TestClass_0829 {}
+    public static class TestClass_0830 {}
+    public static class TestClass_0831 {}
+    public static class TestClass_0832 {}
+    public static class TestClass_0833 {}
+    public static class TestClass_0834 {}
+    public static class TestClass_0835 {}
+    public static class TestClass_0836 {}
+    public static class TestClass_0837 {}
+    public static class TestClass_0838 {}
+    public static class TestClass_0839 {}
+    public static class TestClass_0840 {}
+    public static class TestClass_0841 {}
+    public static class TestClass_0842 {}
+    public static class TestClass_0843 {}
+    public static class TestClass_0844 {}
+    public static class TestClass_0845 {}
+    public static class TestClass_0846 {}
+    public static class TestClass_0847 {}
+    public static class TestClass_0848 {}
+    public static class TestClass_0849 {}
+    public static class TestClass_0850 {}
+    public static class TestClass_0851 {}
+    public static class TestClass_0852 {}
+    public static class TestClass_0853 {}
+    public static class TestClass_0854 {}
+    public static class TestClass_0855 {}
+    public static class TestClass_0856 {}
+    public static class TestClass_0857 {}
+    public static class TestClass_0858 {}
+    public static class TestClass_0859 {}
+    public static class TestClass_0860 {}
+    public static class TestClass_0861 {}
+    public static class TestClass_0862 {}
+    public static class TestClass_0863 {}
+    public static class TestClass_0864 {}
+    public static class TestClass_0865 {}
+    public static class TestClass_0866 {}
+    public static class TestClass_0867 {}
+    public static class TestClass_0868 {}
+    public static class TestClass_0869 {}
+    public static class TestClass_0870 {}
+    public static class TestClass_0871 {}
+    public static class TestClass_0872 {}
+    public static class TestClass_0873 {}
+    public static class TestClass_0874 {}
+    public static class TestClass_0875 {}
+    public static class TestClass_0876 {}
+    public static class TestClass_0877 {}
+    public static class TestClass_0878 {}
+    public static class TestClass_0879 {}
+    public static class TestClass_0880 {}
+    public static class TestClass_0881 {}
+    public static class TestClass_0882 {}
+    public static class TestClass_0883 {}
+    public static class TestClass_0884 {}
+    public static class TestClass_0885 {}
+    public static class TestClass_0886 {}
+    public static class TestClass_0887 {}
+    public static class TestClass_0888 {}
+    public static class TestClass_0889 {}
+    public static class TestClass_0890 {}
+    public static class TestClass_0891 {}
+    public static class TestClass_0892 {}
+    public static class TestClass_0893 {}
+    public static class TestClass_0894 {}
+    public static class TestClass_0895 {}
+    public static class TestClass_0896 {}
+    public static class TestClass_0897 {}
+    public static class TestClass_0898 {}
+    public static class TestClass_0899 {}
+    public static class TestClass_0900 {}
+    public static class TestClass_0901 {}
+    public static class TestClass_0902 {}
+    public static class TestClass_0903 {}
+    public static class TestClass_0904 {}
+    public static class TestClass_0905 {}
+    public static class TestClass_0906 {}
+    public static class TestClass_0907 {}
+    public static class TestClass_0908 {}
+    public static class TestClass_0909 {}
+    public static class TestClass_0910 {}
+    public static class TestClass_0911 {}
+    public static class TestClass_0912 {}
+    public static class TestClass_0913 {}
+    public static class TestClass_0914 {}
+    public static class TestClass_0915 {}
+    public static class TestClass_0916 {}
+    public static class TestClass_0917 {}
+    public static class TestClass_0918 {}
+    public static class TestClass_0919 {}
+    public static class TestClass_0920 {}
+    public static class TestClass_0921 {}
+    public static class TestClass_0922 {}
+    public static class TestClass_0923 {}
+    public static class TestClass_0924 {}
+    public static class TestClass_0925 {}
+    public static class TestClass_0926 {}
+    public static class TestClass_0927 {}
+    public static class TestClass_0928 {}
+    public static class TestClass_0929 {}
+    public static class TestClass_0930 {}
+    public static class TestClass_0931 {}
+    public static class TestClass_0932 {}
+    public static class TestClass_0933 {}
+    public static class TestClass_0934 {}
+    public static class TestClass_0935 {}
+    public static class TestClass_0936 {}
+    public static class TestClass_0937 {}
+    public static class TestClass_0938 {}
+    public static class TestClass_0939 {}
+    public static class TestClass_0940 {}
+    public static class TestClass_0941 {}
+    public static class TestClass_0942 {}
+    public static class TestClass_0943 {}
+    public static class TestClass_0944 {}
+    public static class TestClass_0945 {}
+    public static class TestClass_0946 {}
+    public static class TestClass_0947 {}
+    public static class TestClass_0948 {}
+    public static class TestClass_0949 {}
+    public static class TestClass_0950 {}
+    public static class TestClass_0951 {}
+    public static class TestClass_0952 {}
+    public static class TestClass_0953 {}
+    public static class TestClass_0954 {}
+    public static class TestClass_0955 {}
+    public static class TestClass_0956 {}
+    public static class TestClass_0957 {}
+    public static class TestClass_0958 {}
+    public static class TestClass_0959 {}
+    public static class TestClass_0960 {}
+    public static class TestClass_0961 {}
+    public static class TestClass_0962 {}
+    public static class TestClass_0963 {}
+    public static class TestClass_0964 {}
+    public static class TestClass_0965 {}
+    public static class TestClass_0966 {}
+    public static class TestClass_0967 {}
+    public static class TestClass_0968 {}
+    public static class TestClass_0969 {}
+    public static class TestClass_0970 {}
+    public static class TestClass_0971 {}
+    public static class TestClass_0972 {}
+    public static class TestClass_0973 {}
+    public static class TestClass_0974 {}
+    public static class TestClass_0975 {}
+    public static class TestClass_0976 {}
+    public static class TestClass_0977 {}
+    public static class TestClass_0978 {}
+    public static class TestClass_0979 {}
+    public static class TestClass_0980 {}
+    public static class TestClass_0981 {}
+    public static class TestClass_0982 {}
+    public static class TestClass_0983 {}
+    public static class TestClass_0984 {}
+    public static class TestClass_0985 {}
+    public static class TestClass_0986 {}
+    public static class TestClass_0987 {}
+    public static class TestClass_0988 {}
+    public static class TestClass_0989 {}
+    public static class TestClass_0990 {}
+    public static class TestClass_0991 {}
+    public static class TestClass_0992 {}
+    public static class TestClass_0993 {}
+    public static class TestClass_0994 {}
+    public static class TestClass_0995 {}
+    public static class TestClass_0996 {}
+    public static class TestClass_0997 {}
+    public static class TestClass_0998 {}
+    public static class TestClass_0999 {}
+    public static class TestClass_1000 {}
+    public static class TestClass_1001 {}
+    public static class TestClass_1002 {}
+    public static class TestClass_1003 {}
+    public static class TestClass_1004 {}
+    public static class TestClass_1005 {}
+    public static class TestClass_1006 {}
+    public static class TestClass_1007 {}
+    public static class TestClass_1008 {}
+    public static class TestClass_1009 {}
+    public static class TestClass_1010 {}
+    public static class TestClass_1011 {}
+    public static class TestClass_1012 {}
+    public static class TestClass_1013 {}
+    public static class TestClass_1014 {}
+    public static class TestClass_1015 {}
+    public static class TestClass_1016 {}
+    public static class TestClass_1017 {}
+    public static class TestClass_1018 {}
+    public static class TestClass_1019 {}
+    public static class TestClass_1020 {}
+    public static class TestClass_1021 {}
+    public static class TestClass_1022 {}
+    public static class TestClass_1023 {}
+    public static class TestClass_1024 {}
+
+    public void timeConstClassWithConflict(int count) {
+        Class<?> class0001 = TestClass_0001.class;
+        for (int i = 0; i < count; ++i) {
+            $noinline$foo(class0001);  // Prevent LICM on the TestClass_xxxx.class below.
+            $noinline$foo(TestClass_0000.class);
+            $noinline$foo(TestClass_1024.class);
+        }
+    }
+
+    public void timeConstClassWithoutConflict(int count) {
+        Class<?> class0000 = TestClass_0000.class;
+        for (int i = 0; i < count; ++i) {
+            $noinline$foo(class0000);  // Prevent LICM on the TestClass_xxxx.class below.
+            $noinline$foo(TestClass_0001.class);
+            $noinline$foo(TestClass_1023.class);
+        }
+    }
+
+    static void $noinline$foo(Class<?> s) {
+        if (doThrow) { throw new Error(); }
+    }
+
+    public static boolean doThrow = false;
+}
diff --git a/benchmark/const-string/src/ConstStringBenchmark.java b/benchmark/const-string/src/ConstStringBenchmark.java
index 2beb0a4..2359a5f 100644
--- a/benchmark/const-string/src/ConstStringBenchmark.java
+++ b/benchmark/const-string/src/ConstStringBenchmark.java
@@ -18,6 +18,7 @@
     // 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.
+    // Note: n == n + 1024 (mod 2^10), n + 1 != n + 1023 (mod 2^10).
     public static final String string_0000 = "TestString_0000";
     public static final String string_0001 = "TestString_0001";
     public static final String string_0002 = "TestString_0002";
@@ -1045,21 +1046,21 @@
     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");
-      }
+        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");
-      }
+        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(); }
+        if (doThrow) { throw new Error(); }
     }
 
     public static boolean doThrow = false;
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c785bef..9902628 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -25,6 +25,7 @@
 GTEST_DEX_DIRECTORIES := \
   AbstractMethod \
   AllFields \
+  DexToDexDecompiler \
   ExceptionHandle \
   GetMethodSignature \
   ImageLayoutA \
@@ -106,6 +107,7 @@
 ART_GTEST_transaction_test_DEX_DEPS := Transaction
 ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
 ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
+ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
 
 # The elf writer test has dependencies on core.oat.
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
@@ -114,10 +116,14 @@
 ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_optimizing_pic_64) \
   $(HOST_CORE_IMAGE_optimizing_pic_32) \
+  $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
+  $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
   $(HOST_OUT_EXECUTABLES)/patchoatd
 ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_optimizing_pic_64) \
   $(TARGET_CORE_IMAGE_optimizing_pic_32) \
+  $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
+  $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
   $(TARGET_OUT_EXECUTABLES)/patchoatd
 
 ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 3f55eef..156ca9e 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -496,11 +496,7 @@
 struct XGcOption {
   // These defaults are used when the command line arguments for -Xgc:
   // are either omitted completely or partially.
-  gc::CollectorType collector_type_ = kUseReadBarrier ?
-                                           // If RB is enabled (currently a build-time decision),
-                                           // use CC as the default GC.
-                                           gc::kCollectorTypeCC :
-                                           gc::kCollectorTypeDefault;
+  gc::CollectorType collector_type_ = gc::kCollectorTypeDefault;
   bool verify_pre_gc_heap_ = false;
   bool verify_pre_sweeping_heap_ = kIsDebugBuild;
   bool verify_post_gc_heap_ = false;
@@ -580,10 +576,6 @@
     : background_collector_type_(background_collector_type) {}
   BackgroundGcOption()
     : background_collector_type_(gc::kCollectorTypeNone) {
-
-    if (kUseReadBarrier) {
-      background_collector_type_ = gc::kCollectorTypeCCBackground;  // Background compaction for CC.
-    }
   }
 
   operator gc::CollectorType() const { return background_collector_type_; }
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 6edb639..b883e08 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -28,6 +28,7 @@
         "compiled_method.cc",
         "debug/elf_debug_writer.cc",
         "dex/dex_to_dex_compiler.cc",
+        "dex/dex_to_dex_decompiler.cc",
         "dex/verified_method.cc",
         "dex/verification_results.cc",
         "dex/quick_compiler_callbacks.cc",
@@ -52,6 +53,7 @@
         "optimizing/code_generator_utils.cc",
         "optimizing/constant_folding.cc",
         "optimizing/dead_code_elimination.cc",
+        "optimizing/escape.cc",
         "optimizing/graph_checker.cc",
         "optimizing/graph_visualizer.cc",
         "optimizing/gvn.cc",
@@ -253,7 +255,10 @@
             },
         },
     },
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libart-dexlayout",
+    ],
 }
 
 art_cc_library {
@@ -290,7 +295,10 @@
             },
         },
     },
-    shared_libs: ["libartd"],
+    shared_libs: [
+        "libartd",
+        "libartd-dexlayout"
+    ],
 }
 
 art_cc_library {
@@ -312,6 +320,7 @@
     srcs: [
         "compiled_method_test.cc",
         "debug/dwarf/dwarf_test.cc",
+        "dex/dex_to_dex_decompiler_test.cc",
         "driver/compiled_method_storage_test.cc",
         "driver/compiler_driver_test.cc",
         "elf_writer_test.cc",
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 99b0ac1..174e85e 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -26,6 +26,7 @@
 #include "base/array_ref.h"
 #include "base/bit_utils.h"
 #include "base/length_prefixed_array.h"
+#include "dex_file_types.h"
 #include "method_reference.h"
 
 namespace art {
@@ -302,9 +303,9 @@
     return target_dex_file_;
   }
 
-  uint32_t TargetTypeIndex() const {
+  dex::TypeIndex TargetTypeIndex() const {
     DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative);
-    return type_idx_;
+    return dex::TypeIndex(type_idx_);
   }
 
   const DexFile* TargetStringDexFile() const {
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 9c1d72b..cf69f46 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -233,6 +233,8 @@
                  << " by replacing it with 2 NOPs at dex pc "
                  << StringPrintf("0x%x", dex_pc) << " in method "
                  << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
+  quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
+  quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
   // We are modifying 4 consecutive bytes.
   inst->SetOpcode(Instruction::NOP);
   inst->SetVRegA_10x(0u);  // keep compliant with verifier.
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 3fad6d4..0a00d45 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -17,8 +17,6 @@
 #ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
 #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
 
-#include "jni.h"
-
 #include "dex_file.h"
 #include "invoke_type.h"
 
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
new file mode 100644
index 0000000..051125e
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -0,0 +1,198 @@
+/*
+ * 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 "dex_to_dex_decompiler.h"
+
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "optimizing/bytecode_utils.h"
+
+namespace art {
+namespace optimizer {
+
+class DexDecompiler {
+ public:
+  DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+    : code_item_(code_item),
+      quickened_info_ptr_(quickened_info.data()),
+      quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+
+  bool Decompile();
+
+ private:
+  void DecompileInstanceFieldAccess(Instruction* inst,
+                                    uint32_t dex_pc,
+                                    Instruction::Code new_opcode) {
+    uint16_t index = GetIndexAt(dex_pc);
+    inst->SetOpcode(new_opcode);
+    inst->SetVRegC_22c(index);
+  }
+
+  void DecompileInvokeVirtual(Instruction* inst,
+                              uint32_t dex_pc,
+                              Instruction::Code new_opcode,
+                              bool is_range) {
+    uint16_t index = GetIndexAt(dex_pc);
+    inst->SetOpcode(new_opcode);
+    if (is_range) {
+      inst->SetVRegB_3rc(index);
+    } else {
+      inst->SetVRegB_35c(index);
+    }
+  }
+
+  void DecompileNop(Instruction* inst, uint32_t dex_pc) {
+    if (quickened_info_ptr_ == quickened_info_end_) {
+      return;
+    }
+    const uint8_t* temporary_pointer = quickened_info_ptr_;
+    uint32_t quickened_pc = DecodeUnsignedLeb128(&temporary_pointer);
+    if (quickened_pc != dex_pc) {
+      return;
+    }
+    uint16_t reference_index = GetIndexAt(dex_pc);
+    uint16_t type_index = GetIndexAt(dex_pc);
+    inst->SetOpcode(Instruction::CHECK_CAST);
+    inst->SetVRegA_21c(reference_index);
+    inst->SetVRegB_21c(type_index);
+  }
+
+  uint16_t GetIndexAt(uint32_t dex_pc) {
+    // Note that as a side effect, DecodeUnsignedLeb128 update the given pointer
+    // to the new position in the buffer.
+    DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
+    uint32_t quickened_pc = DecodeUnsignedLeb128(&quickened_info_ptr_);
+    DCHECK_LT(quickened_info_ptr_, quickened_info_end_);
+    uint16_t index = DecodeUnsignedLeb128(&quickened_info_ptr_);
+    DCHECK_LE(quickened_info_ptr_, quickened_info_end_);
+    DCHECK_EQ(quickened_pc, dex_pc);
+    return index;
+  }
+
+  const DexFile::CodeItem& code_item_;
+  const uint8_t* quickened_info_ptr_;
+  const uint8_t* const quickened_info_end_;
+
+  DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
+};
+
+bool DexDecompiler::Decompile() {
+  // We need to iterate over the code item, and not over the quickening data,
+  // because the RETURN_VOID quickening is not encoded in the quickening data. Because
+  // unquickening is a rare need and not performance sensitive, it is not worth the
+  // added storage to also add the RETURN_VOID quickening in the quickened data.
+  for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+    uint32_t dex_pc = it.CurrentDexPc();
+    Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+
+    switch (inst->Opcode()) {
+      case Instruction::RETURN_VOID_NO_BARRIER:
+        inst->SetOpcode(Instruction::RETURN_VOID);
+        break;
+
+      case Instruction::NOP:
+        DecompileNop(inst, dex_pc);
+        break;
+
+      case Instruction::IGET_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET);
+        break;
+
+      case Instruction::IGET_WIDE_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE);
+        break;
+
+      case Instruction::IGET_OBJECT_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT);
+        break;
+
+      case Instruction::IGET_BOOLEAN_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN);
+        break;
+
+      case Instruction::IGET_BYTE_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE);
+        break;
+
+      case Instruction::IGET_CHAR_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR);
+        break;
+
+      case Instruction::IGET_SHORT_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT);
+        break;
+
+      case Instruction::IPUT_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT);
+        break;
+
+      case Instruction::IPUT_BOOLEAN_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN);
+        break;
+
+      case Instruction::IPUT_BYTE_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE);
+        break;
+
+      case Instruction::IPUT_CHAR_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR);
+        break;
+
+      case Instruction::IPUT_SHORT_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT);
+        break;
+
+      case Instruction::IPUT_WIDE_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE);
+        break;
+
+      case Instruction::IPUT_OBJECT_QUICK:
+        DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT);
+        break;
+
+      case Instruction::INVOKE_VIRTUAL_QUICK:
+        DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL, false);
+        break;
+
+      case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+        DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE, true);
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  if (quickened_info_ptr_ != quickened_info_end_) {
+    LOG(ERROR) << "Failed to use all values in quickening info."
+               << " Actual: " << std::hex << quickened_info_ptr_
+               << " Expected: " << quickened_info_end_;
+    return false;
+  }
+
+  return true;
+}
+
+bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
+                     const ArrayRef<const uint8_t>& quickened_info) {
+  DexDecompiler decompiler(code_item, quickened_info);
+  return decompiler.Decompile();
+}
+
+}  // namespace optimizer
+}  // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
new file mode 100644
index 0000000..5502ca2
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -0,0 +1,38 @@
+/*
+ * 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_DEX_DEX_TO_DEX_DECOMPILER_H_
+#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+
+#include "base/array_ref.h"
+#include "dex_file.h"
+
+namespace art {
+namespace optimizer {
+
+// "Decompile", that is unquicken, the code item provided, given the
+// associated quickening data.
+// TODO: code_item isn't really a const element, but changing it
+// to non-const has too many repercussions on the code base. We make it
+// consistent with DexToDexCompiler, but we should really change it to
+// DexFile::CodeItem*.
+bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
+                     const ArrayRef<const uint8_t>& quickened_data);
+
+}  // namespace optimizer
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
new file mode 100644
index 0000000..ea6c7a2
--- /dev/null
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -0,0 +1,136 @@
+/*
+ * 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 "dex/dex_to_dex_decompiler.h"
+
+#include "class_linker.h"
+#include "compiler/common_compiler_test.h"
+#include "compiler/compiled_method.h"
+#include "compiler/driver/compiler_options.h"
+#include "compiler/driver/compiler_driver.h"
+#include "compiler_callbacks.h"
+#include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "verifier/method_verifier-inl.h"
+#include "mirror/class_loader.h"
+#include "runtime.h"
+#include "thread.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+class DexToDexDecompilerTest : public CommonCompilerTest {
+ public:
+  void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+    TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
+    TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+    compiler_options_->boot_image_ = false;
+    compiler_options_->SetCompilerFilter(CompilerFilter::kInterpretOnly);
+    compiler_driver_->CompileAll(class_loader,
+                                 GetDexFiles(class_loader),
+                                 /* verifier_deps */ nullptr,
+                                 &timings);
+  }
+
+  void RunTest(const char* dex_name) {
+    Thread* self = Thread::Current();
+    // First load the original dex file.
+    jobject original_class_loader;
+    {
+      ScopedObjectAccess soa(self);
+      original_class_loader = LoadDex(dex_name);
+    }
+    const DexFile* original_dex_file = GetDexFiles(original_class_loader)[0];
+
+    // Load the dex file again and make it writable to quicken them.
+    jobject class_loader;
+    const DexFile* updated_dex_file = nullptr;
+    {
+      ScopedObjectAccess soa(self);
+      class_loader = LoadDex(dex_name);
+      updated_dex_file = GetDexFiles(class_loader)[0];
+      Runtime::Current()->GetClassLinker()->RegisterDexFile(
+          *updated_dex_file, soa.Decode<mirror::ClassLoader>(class_loader).Ptr());
+    }
+    // The dex files should be identical.
+    int cmp = memcmp(original_dex_file->Begin(),
+                     updated_dex_file->Begin(),
+                     updated_dex_file->Size());
+    ASSERT_EQ(0, cmp);
+
+    updated_dex_file->EnableWrite();
+    CompileAll(class_loader);
+    // The dex files should be different after quickening.
+    cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
+    ASSERT_NE(0, cmp);
+
+    // Unquicken the dex file.
+    for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i);
+      const uint8_t* class_data = updated_dex_file->GetClassData(class_def);
+      if (class_data == nullptr) {
+        continue;
+      }
+      ClassDataItemIterator it(*updated_dex_file, class_data);
+      // Skip fields
+      while (it.HasNextStaticField()) {
+        it.Next();
+      }
+      while (it.HasNextInstanceField()) {
+        it.Next();
+      }
+
+      // Unquicken each method.
+      while (it.HasNextDirectMethod()) {
+        uint32_t method_idx = it.GetMemberIndex();
+        CompiledMethod* compiled_method =
+            compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+        ArrayRef<const uint8_t> table;
+        if (compiled_method != nullptr) {
+          table = compiled_method->GetVmapTable();
+        }
+        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        it.Next();
+      }
+      while (it.HasNextVirtualMethod()) {
+        uint32_t method_idx = it.GetMemberIndex();
+        CompiledMethod* compiled_method =
+            compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
+        ArrayRef<const uint8_t> table;
+        if (compiled_method != nullptr) {
+          table = compiled_method->GetVmapTable();
+        }
+        optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
+    }
+
+    // Make sure after unquickening we go back to the same contents as the original dex file.
+    cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size());
+    ASSERT_EQ(0, cmp);
+  }
+};
+
+TEST_F(DexToDexDecompilerTest, VerifierDeps) {
+  RunTest("VerifierDeps");
+}
+
+TEST_F(DexToDexDecompilerTest, DexToDexDecompiler) {
+  RunTest("DexToDexDecompiler");
+}
+
+}  // namespace art
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 511a787..3fb10d8 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -31,49 +31,68 @@
 VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
     : compiler_options_(compiler_options),
       verified_methods_lock_("compiler verified methods lock"),
-      verified_methods_(),
-      rejected_classes_lock_("compiler rejected classes lock"),
-      rejected_classes_() {
-}
+      rejected_classes_lock_("compiler rejected classes lock") {}
 
 VerificationResults::~VerificationResults() {
-  Thread* self = Thread::Current();
-  {
-    WriterMutexLock mu(self, verified_methods_lock_);
-    STLDeleteValues(&verified_methods_);
-  }
+  WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+  DeleteResults(preregistered_dex_files_);
+  STLDeleteValues(&verified_methods_);
 }
 
 void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
   DCHECK(method_verifier != nullptr);
   MethodReference ref = method_verifier->GetMethodReference();
   bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
-  const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
+  std::unique_ptr<const VerifiedMethod> verified_method(
+      VerifiedMethod::Create(method_verifier, compile));
   if (verified_method == nullptr) {
     // We'll punt this later.
     return;
   }
-
-  WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
-  auto it = verified_methods_.find(ref);
-  if (it != verified_methods_.end()) {
+  bool inserted;
+  DexFileMethodArray* const array = GetMethodArray(ref.dex_file);
+  const VerifiedMethod* existing = nullptr;
+  if (array != nullptr) {
+    DCHECK(array != nullptr);
+    Atomic<const VerifiedMethod*>* slot = &(*array)[ref.dex_method_index];
+    inserted = slot->CompareExchangeStrongSequentiallyConsistent(nullptr, verified_method.get());
+    if (!inserted) {
+      existing = slot->LoadSequentiallyConsistent();
+      DCHECK_NE(verified_method.get(), existing);
+    }
+  } else {
+    WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+    auto it = verified_methods_.find(ref);
+    inserted = it == verified_methods_.end();
+    if (inserted) {
+      verified_methods_.Put(ref, verified_method.get());
+      DCHECK(verified_methods_.find(ref) != verified_methods_.end());
+    } else {
+      existing = it->second;
+    }
+  }
+  if (inserted) {
+    // Successfully added, release the unique_ptr since we no longer have ownership.
+    DCHECK_EQ(GetVerifiedMethod(ref), verified_method.get());
+    verified_method.release();
+  } else {
     // TODO: Investigate why are we doing the work again for this method and try to avoid it.
     LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod();
     if (!Runtime::Current()->UseJitCompilation()) {
-      DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
-      DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
+      DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
+      DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
     }
-    // Delete the new verified method since there was already an existing one registered. It
-    // is unsafe to replace the existing one since the JIT may be using it to generate a
-    // native GC map.
-    delete verified_method;
-    return;
+    // Let the unique_ptr delete the new verified method since there was already an existing one
+    // registered. It is unsafe to replace the existing one since the JIT may be using it to
+    // generate a native GC map.
   }
-  verified_methods_.Put(ref, verified_method);
-  DCHECK(verified_methods_.find(ref) != verified_methods_.end());
 }
 
 const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
+  DexFileMethodArray* array = GetMethodArray(ref.dex_file);
+  if (array != nullptr) {
+    return (*array)[ref.dex_method_index].LoadRelaxed();
+  }
   ReaderMutexLock mu(Thread::Current(), verified_methods_lock_);
   auto it = verified_methods_.find(ref);
   return (it != verified_methods_.end()) ? it->second : nullptr;
@@ -105,4 +124,42 @@
   return true;
 }
 
+void VerificationResults::PreRegisterDexFile(const DexFile* dex_file) {
+  CHECK(preregistered_dex_files_.find(dex_file) == preregistered_dex_files_.end())
+      << dex_file->GetLocation();
+  DexFileMethodArray array(dex_file->NumMethodIds());
+  WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+  // There can be some verified methods that are already registered for the dex_file since we set
+  // up well known classes earlier. Remove these and put them in the array so that we don't
+  // accidentally miss seeing them.
+  for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) {
+    MethodReference ref = it->first;
+    if (ref.dex_file == dex_file) {
+      array[ref.dex_method_index].StoreSequentiallyConsistent(it->second);
+      it = verified_methods_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+  preregistered_dex_files_.emplace(dex_file, std::move(array));
+}
+
+void VerificationResults::DeleteResults(DexFileResults& array) {
+  for (auto& pair : array) {
+    for (Atomic<const VerifiedMethod*>& method : pair.second) {
+      delete method.LoadSequentiallyConsistent();
+    }
+  }
+  array.clear();
+}
+
+VerificationResults::DexFileMethodArray* VerificationResults::GetMethodArray(
+    const DexFile* dex_file) {
+  auto it = preregistered_dex_files_.find(dex_file);
+  if (it != preregistered_dex_files_.end()) {
+    return &it->second;
+  }
+  return nullptr;
+}
+
 }  // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 6afd1ab..b3356e0 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -19,8 +19,8 @@
 
 #include <stdint.h>
 #include <set>
-#include <vector>
 
+#include "base/dchecked_vector.h"
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "class_reference.h"
@@ -38,35 +38,48 @@
 
 // Used by CompilerCallbacks to track verification information from the Runtime.
 class VerificationResults {
-  public:
-    explicit VerificationResults(const CompilerOptions* compiler_options);
-    ~VerificationResults();
+ public:
+  explicit VerificationResults(const CompilerOptions* compiler_options);
+  ~VerificationResults();
 
-    void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
-        REQUIRES_SHARED(Locks::mutator_lock_)
-        REQUIRES(!verified_methods_lock_);
+  void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!verified_methods_lock_);
 
-    const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
-        REQUIRES(!verified_methods_lock_);
+  const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
+      REQUIRES(!verified_methods_lock_);
 
-    void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
-    bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+  void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+  bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
 
-    bool IsCandidateForCompilation(MethodReference& method_ref,
-                                   const uint32_t access_flags);
+  bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags);
 
-  private:
-    const CompilerOptions* const compiler_options_;
+  // Add a dex file array to the preregistered_dex_files_ array. These dex files require no locks to
+  // access. It is not safe to call if other callers are calling GetVerifiedMethod concurrently.
+  void PreRegisterDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
 
-    // Verified methods.
-    typedef SafeMap<MethodReference, const VerifiedMethod*,
-        MethodReferenceComparator> VerifiedMethodMap;
-    ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-    VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
+ private:
+  // Verified methods. The method array is fixed to avoid needing a lock to extend it.
+  using DexFileMethodArray = dchecked_vector<Atomic<const VerifiedMethod*>>;
+  using DexFileResults = std::map<const DexFile*, DexFileMethodArray>;
+  using VerifiedMethodMap = SafeMap<MethodReference,
+                                    const VerifiedMethod*,
+                                    MethodReferenceComparator>;
 
-    // Rejected classes.
-    ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-    std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+  static void DeleteResults(DexFileResults& array);
+
+  DexFileMethodArray* GetMethodArray(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
+  VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
+  const CompilerOptions* const compiler_options_;
+
+  // Dex2oat can preregister dex files to avoid locking when calling GetVerifiedMethod.
+  DexFileResults preregistered_dex_files_;
+
+  ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+
+  // Rejected classes.
+  ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
 };
 
 }  // namespace art
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index e19fb7b..1bdace9 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -230,7 +230,7 @@
         const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
                                                                 inst->VRegA_21c()));
         const verifier::RegType& cast_type =
-            method_verifier->ResolveCheckedClass(inst->VRegB_21c());
+            method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
         is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type, method_verifier);
       } else {
         const verifier::RegType& array_type(line->GetRegisterType(method_verifier,
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 9711516..f056dd3 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -38,7 +38,7 @@
 
 inline mirror::Class* CompilerDriver::ResolveClass(
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-    Handle<mirror::ClassLoader> class_loader, uint16_t cls_index,
+    Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index,
     const DexCompilationUnit* mUnit) {
   DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
   DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
@@ -141,7 +141,7 @@
     mirror::Class* referrer_class,
     ArtMember* resolved_member,
     uint16_t member_idx,
-    uint32_t* storage_index) {
+    dex::TypeIndex* storage_index) {
   DCHECK(resolved_member->IsStatic());
   if (LIKELY(referrer_class != nullptr)) {
     ObjPtr<mirror::Class> members_class = resolved_member->GetDeclaringClass();
@@ -156,7 +156,7 @@
       // TODO: for images we can elide the static storage base null check
       // if we know there's a non-null entry in the image
       const DexFile* dex_file = dex_cache->GetDexFile();
-      uint32_t storage_idx = DexFile::kDexNoIndex;
+      dex::TypeIndex storage_idx(DexFile::kDexNoIndex16);
       if (LIKELY(members_class->GetDexCache() == dex_cache)) {
         // common case where the dex cache of both the referrer and the member are the same,
         // no need to search the dex file
@@ -166,27 +166,27 @@
         // of the class mentioned in the dex file and there is no dex cache entry.
         storage_idx = resolved_member->GetDeclaringClass()->FindTypeIndexInOtherDexFile(*dex_file);
       }
-      if (storage_idx != DexFile::kDexNoIndex) {
+      if (storage_idx.IsValid()) {
         *storage_index = storage_idx;
         return std::make_pair(true, !resolved_member->IsFinal());
       }
     }
   }
   // Conservative defaults.
-  *storage_index = DexFile::kDexNoIndex;
+  *storage_index = dex::TypeIndex(DexFile::kDexNoIndex16);
   return std::make_pair(false, false);
 }
 
 inline std::pair<bool, bool> CompilerDriver::IsFastStaticField(
     mirror::DexCache* dex_cache, mirror::Class* referrer_class,
-    ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index) {
+    ArtField* resolved_field, uint16_t field_idx, dex::TypeIndex* storage_index) {
   return IsClassOfStaticMemberAvailableToReferrer(
       dex_cache, referrer_class, resolved_field, field_idx, storage_index);
 }
 
 inline bool CompilerDriver::IsClassOfStaticMethodAvailableToReferrer(
     mirror::DexCache* dex_cache, mirror::Class* referrer_class,
-    ArtMethod* resolved_method, uint16_t method_idx, uint32_t* storage_index) {
+    ArtMethod* resolved_method, uint16_t method_idx, dex::TypeIndex* storage_index) {
   std::pair<bool, bool> result = IsClassOfStaticMemberAvailableToReferrer(
       dex_cache, referrer_class, resolved_method, method_idx, storage_index);
   // Only the first member of `result` is meaningful, as there is no
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 223be88..ad75ec4 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -45,6 +45,7 @@
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex/dex_to_dex_compiler.h"
+#include "dex/dex_to_dex_decompiler.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
 #include "driver/compiler_options.h"
@@ -72,6 +73,7 @@
 #include "transaction.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
 #include "utils/swap_space.h"
+#include "vdex_file.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 #include "verifier/verifier_log_mode.h"
@@ -394,7 +396,6 @@
 
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                verifier::VerifierDeps* verifier_deps,
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
 
@@ -406,7 +407,7 @@
   // 2) Resolve all classes
   // 3) Attempt to verify all classes
   // 4) Attempt to initialize image classes, and trivially initialized classes
-  PreCompile(class_loader, dex_files, verifier_deps, timings);
+  PreCompile(class_loader, dex_files, timings);
   if (GetCompilerOptions().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
@@ -433,6 +434,72 @@
   FreeThreadPools();
 }
 
+// In-place unquicken the given `dex_files` based on `quickening_info`.
+static void Unquicken(const std::vector<const DexFile*>& dex_files,
+                      const ArrayRef<const uint8_t>& quickening_info) {
+  const uint8_t* quickening_info_ptr = quickening_info.data();
+  const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
+  for (const DexFile* dex_file : dex_files) {
+    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+      const uint8_t* class_data = dex_file->GetClassData(class_def);
+      if (class_data == nullptr) {
+        continue;
+      }
+      ClassDataItemIterator it(*dex_file, class_data);
+      // Skip fields
+      while (it.HasNextStaticField()) {
+        it.Next();
+      }
+      while (it.HasNextInstanceField()) {
+        it.Next();
+      }
+
+      // Unquicken each method.
+      while (it.HasNextDirectMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(
+              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+
+      while (it.HasNextVirtualMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(
+              *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
+    }
+  }
+  DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info";
+}
+
+void CompilerDriver::CompileAll(jobject class_loader,
+                                const std::vector<const DexFile*>& dex_files,
+                                VdexFile* vdex_file,
+                                TimingLogger* timings) {
+  if (vdex_file != nullptr) {
+    // TODO: we unquicken unconditionnally, as we don't know
+    // if the boot image has changed. How exactly we'll know is under
+    // experimentation.
+    Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+    Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+        new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
+  }
+  CompileAll(class_loader, dex_files, timings);
+}
+
 static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
     Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
     const DexFile& dex_file, const DexFile::ClassDef& class_def)
@@ -538,14 +605,9 @@
               : optimizer::DexToDexCompilationLevel::kRequired);
     }
   } else if ((access_flags & kAccNative) != 0) {
-    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) {
+    // Are we extracting only and have support for generic JNI down calls?
+    if (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
+        InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
       // Leaving this empty will trigger the generic JNI version
     } else {
       // Look-up the ArtMethod associated with this code_item (if any)
@@ -678,7 +740,7 @@
 
   InitializeThreadPools();
 
-  PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings);
+  PreCompile(jclass_loader, dex_files, timings);
 
   // Can we run DEX-to-DEX compiler on this class ?
   optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -875,7 +937,6 @@
 
 void CompilerDriver::PreCompile(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                verifier::VerifierDeps* verifier_deps,
                                 TimingLogger* timings) {
   CheckThreadPools();
 
@@ -909,7 +970,7 @@
     VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
   }
 
-  Verify(class_loader, dex_files, verifier_deps, timings);
+  Verify(class_loader, dex_files, timings);
   VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -973,11 +1034,12 @@
     return true;
   }
   DCHECK(profile_compilation_info_ != nullptr);
-  bool result = profile_compilation_info_->ContainsClass(dex_file, class_idx);
+  const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_idx);
+  dex::TypeIndex type_idx = class_def.class_idx_;
+  bool result = profile_compilation_info_->ContainsClass(dex_file, type_idx);
   if (kDebugProfileGuidedCompilation) {
-    LOG(INFO) << "[ProfileGuidedCompilation] "
-        << (result ? "Verified" : "Skipped") << " method:"
-        << dex_file.GetClassDescriptor(dex_file.GetClassDef(class_idx));
+    LOG(INFO) << "[ProfileGuidedCompilation] " << (result ? "Verified" : "Skipped") << " method:"
+        << dex_file.GetClassDescriptor(class_def);
   }
   return result;
 }
@@ -985,7 +1047,7 @@
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
   explicit ResolveCatchBlockExceptionsClassVisitor(
-      std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
+      std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve)
      : exceptions_to_resolve_(exceptions_to_resolve) {}
 
   virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1016,8 +1078,8 @@
         has_catch_all = true;
       }
       for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
-        uint16_t encoded_catch_handler_handlers_type_idx =
-            DecodeUnsignedLeb128(&encoded_catch_handler_list);
+        dex::TypeIndex encoded_catch_handler_handlers_type_idx =
+            dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
         // Add to set of types to resolve if not already in the dex cache resolved types
         if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
                                               pointer_size)) {
@@ -1034,7 +1096,7 @@
     }
   }
 
-  std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve_;
+  std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_;
 };
 
 class RecordImageClassesVisitor : public ClassVisitor {
@@ -1082,7 +1144,7 @@
   // Resolve exception classes referenced by the loaded classes. The catch logic assumes
   // exceptions are resolved by the verifier when there is a catch block in an interested method.
   // Do this here so that exception classes appear to have been specified image classes.
-  std::set<std::pair<uint16_t, const DexFile*>> unresolved_exception_types;
+  std::set<std::pair<dex::TypeIndex, const DexFile*>> unresolved_exception_types;
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> java_lang_Throwable(
       hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
@@ -1090,8 +1152,8 @@
     unresolved_exception_types.clear();
     ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
     class_linker->VisitClasses(&visitor);
-    for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) {
-      uint16_t exception_type_idx = exception_type.first;
+    for (const auto& exception_type : unresolved_exception_types) {
+      dex::TypeIndex exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
       StackHandleScope<2> hs2(self);
       Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
@@ -1342,7 +1404,7 @@
 
 bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
                                                 Handle<mirror::DexCache> dex_cache,
-                                                uint32_t type_idx) {
+                                                dex::TypeIndex type_idx) {
   // Get type from dex cache assuming it was populated by the verifier
   mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == nullptr) {
@@ -1371,7 +1433,7 @@
 
 bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
                                                             Handle<mirror::DexCache> dex_cache,
-                                                            uint32_t type_idx,
+                                                            dex::TypeIndex type_idx,
                                                             bool* finalizable) {
   // Get type from dex cache assuming it was populated by the verifier.
   mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
@@ -1865,7 +1927,7 @@
  public:
   explicit ResolveTypeVisitor(const ParallelCompilationManager* manager) : manager_(manager) {
   }
-  virtual void Visit(size_t type_idx) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
+  void Visit(size_t type_idx) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
   // Class derived values are more complicated, they require the linker and loader.
     ScopedObjectAccess soa(Thread::Current());
     ClassLinker* class_linker = manager_->GetClassLinker();
@@ -1876,7 +1938,10 @@
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
         dex_file,
         class_loader.Get())));
-    mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
+    mirror::Class* klass = class_linker->ResolveType(dex_file,
+                                                     dex::TypeIndex(type_idx),
+                                                     dex_cache,
+                                                     class_loader);
 
     if (klass == nullptr) {
       soa.Self()->AssertPendingException();
@@ -1937,8 +2002,10 @@
 
 void CompilerDriver::Verify(jobject jclass_loader,
                             const std::vector<const DexFile*>& dex_files,
-                            verifier::VerifierDeps* verifier_deps,
                             TimingLogger* timings) {
+  verifier::VerifierDeps* verifier_deps =
+      Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+  // If there is an existing `VerifierDeps`, try to use it for fast verification.
   if (verifier_deps != nullptr) {
     TimingLogger::ScopedTiming t("Fast Verify", timings);
     ScopedObjectAccess soa(Thread::Current());
@@ -1956,9 +2023,9 @@
       for (const DexFile* dex_file : dex_files) {
         // Fetch the list of unverified classes and turn it into a set for faster
         // lookups.
-        const std::vector<uint16_t>& unverified_classes =
+        const std::vector<dex::TypeIndex>& unverified_classes =
             verifier_deps->GetUnverifiedClasses(*dex_file);
-        std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end());
+        std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
         for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
           const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
           const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -1976,13 +2043,20 @@
     }
   }
 
-  // If there is no passed `verifier_deps` (because of non-existing vdex), or
-  // the passed `verifier_deps` is not valid anymore, create a new one for
+  // If there is no existing `verifier_deps` (because of non-existing vdex), or
+  // the existing `verifier_deps` is not valid anymore, create a new one for
   // non boot image compilation. The verifier will need it to record the new dependencies.
   // Then dex2oat can update the vdex file with these new dependencies.
   if (!GetCompilerOptions().IsBootImage()) {
-    Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
-        new verifier::VerifierDeps(dex_files));
+    // Create the main VerifierDeps, and set it to this thread.
+    verifier_deps = new verifier::VerifierDeps(dex_files);
+    Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps);
+    Thread::Current()->SetVerifierDeps(verifier_deps);
+    // Create per-thread VerifierDeps to avoid contention on the main one.
+    // We will merge them after verification.
+    for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+      worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
+    }
   }
   // Note: verification should not be pulling in classes anymore when compiling the boot image,
   //       as all should have been resolved before. As such, doing this in parallel should still
@@ -1996,6 +2070,17 @@
                   parallel_thread_count_,
                   timings);
   }
+
+  if (!GetCompilerOptions().IsBootImage()) {
+    // Merge all VerifierDeps into the main one.
+    for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+      verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
+      worker->GetThread()->SetVerifierDeps(nullptr);
+      verifier_deps->MergeWith(*thread_deps, dex_files);;
+      delete thread_deps;
+    }
+    Thread::Current()->SetVerifierDeps(nullptr);
+  }
 }
 
 class VerifyClassVisitor : public CompilationVisitor {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index c8d6cb0..7418b00 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -31,6 +31,7 @@
 #include "class_reference.h"
 #include "compiler.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "driver/compiled_method_storage.h"
 #include "jit/offline_profiling_info.h"
 #include "invoke_type.h"
@@ -50,7 +51,6 @@
 
 namespace verifier {
 class MethodVerifier;
-class VerifierDeps;
 class VerifierDepsTest;
 }  // namespace verifier
 
@@ -68,6 +68,7 @@
 using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
 template<class T> class Handle;
 class TimingLogger;
+class VdexFile;
 class VerificationResults;
 class VerifiedMethod;
 
@@ -118,7 +119,12 @@
 
   void CompileAll(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
-                  verifier::VerifierDeps* verifier_deps,
+                  TimingLogger* timings)
+      REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
+
+  void CompileAll(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  VdexFile* vdex_file,
                   TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
 
@@ -188,14 +194,14 @@
   // Are runtime access checks necessary in the compiled code?
   bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
                                   Handle<mirror::DexCache> dex_cache,
-                                  uint32_t type_idx)
+                                  dex::TypeIndex type_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Are runtime access and instantiable checks necessary in the code?
   // out_is_finalizable is set to whether the type is finalizable.
   bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
                                               Handle<mirror::DexCache> dex_cache,
-                                              uint32_t type_idx,
+                                              dex::TypeIndex type_idx,
                                               bool* out_is_finalizable)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -207,7 +213,7 @@
 
   mirror::Class* ResolveClass(
       const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-      Handle<mirror::ClassLoader> class_loader, uint16_t type_index,
+      Handle<mirror::ClassLoader> class_loader, dex::TypeIndex type_index,
       const DexCompilationUnit* mUnit)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -234,9 +240,11 @@
 
   // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the type index
   // of the declaring class in the referrer's dex file.
-  std::pair<bool, bool> IsFastStaticField(
-      mirror::DexCache* dex_cache, mirror::Class* referrer_class,
-      ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index)
+  std::pair<bool, bool> IsFastStaticField(mirror::DexCache* dex_cache,
+                                          mirror::Class* referrer_class,
+                                          ArtField* resolved_field,
+                                          uint16_t field_idx,
+                                          dex::TypeIndex* storage_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Return whether the declaring class of `resolved_method` is
@@ -248,7 +256,7 @@
                                                 mirror::Class* referrer_class,
                                                 ArtMethod* resolved_method,
                                                 uint16_t method_idx,
-                                                uint32_t* storage_index)
+                                                dex::TypeIndex* storage_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Resolve a method. Returns null on failure, including incompatible class change.
@@ -395,7 +403,7 @@
                                                                  mirror::Class* referrer_class,
                                                                  ArtMember* resolved_member,
                                                                  uint16_t member_idx,
-                                                                 uint32_t* storage_index)
+                                                                 dex::TypeIndex* storage_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Can `referrer_class` access the resolved `member`?
@@ -417,7 +425,6 @@
  private:
   void PreCompile(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
-                  verifier::VerifierDeps* verifier_deps,
                   TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
 
@@ -440,7 +447,6 @@
 
   void Verify(jobject class_loader,
               const std::vector<const DexFile*>& dex_files,
-              verifier::VerifierDeps* verifier_deps,
               TimingLogger* timings);
 
   void VerifyDexFile(jobject class_loader,
@@ -584,6 +590,7 @@
   const BitVector* current_dex_to_dex_methods_;
 
   friend class CompileClassVisitor;
+  friend class DexToDexDecompilerTest;
   friend class verifier::VerifierDepsTest;
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 9679a79..f40c712 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -24,6 +24,7 @@
 #include "class_linker-inl.h"
 #include "common_compiler_test.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "gc/heap.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -115,9 +116,9 @@
   }
   EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
   for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-    mirror::Class* type = dex_cache->GetResolvedType(i);
+    mirror::Class* type = dex_cache->GetResolvedType(dex::TypeIndex(i));
     EXPECT_TRUE(type != nullptr) << "type_idx=" << i
-                              << " " << dex.GetTypeDescriptor(dex.GetTypeId(i));
+                              << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i)));
   }
   EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods());
   auto* cl = Runtime::Current()->GetClassLinker();
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 56b632d..9c62f80 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -341,6 +341,7 @@
   const std::vector<std::string>* passes_to_run_;
 
   friend class Dex2Oat;
+  friend class DexToDexDecompilerTest;
   friend class CommonCompilerTest;
   friend class verifier::VerifierDepsTest;
 
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 31a7529..7c02384 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -879,7 +879,7 @@
     elf_header.e_ident[EI_MAG2]       = ELFMAG2;
     elf_header.e_ident[EI_MAG3]       = ELFMAG3;
     elf_header.e_ident[EI_CLASS]      = (sizeof(Elf_Addr) == sizeof(Elf32_Addr))
-                                         ? ELFCLASS32 : ELFCLASS64;;
+                                         ? ELFCLASS32 : ELFCLASS64;
     elf_header.e_ident[EI_DATA]       = ELFDATA2LSB;
     elf_header.e_ident[EI_VERSION]    = EV_CURRENT;
     elf_header.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index fcb8979..5629dff 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -211,7 +211,9 @@
                                                       &driver->GetCompilerOptions(),
                                                       oat_file.GetFile()));
         elf_writers.back()->Start();
-        oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, &timings));
+        oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true,
+                                               &timings,
+                                               /*profile_compilation_info*/nullptr));
       }
 
       std::vector<OutputStream*> rodata;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 51ef440..a706697 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -32,6 +32,7 @@
 #include "class_linker-inl.h"
 #include "compiled_method.h"
 #include "dex_file-inl.h"
+#include "dex_file_types.h"
 #include "driver/compiler_driver.h"
 #include "elf_file.h"
 #include "elf_utils.h"
@@ -697,7 +698,7 @@
   return true;
 }
 
-class ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
+class ImageWriter::ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
  public:
   bool operator()(ObjPtr<Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
     StackHandleScope<1> hs(Thread::Current());
@@ -837,7 +838,7 @@
   return true;
 }
 
-class NonImageClassesVisitor : public ClassVisitor {
+class ImageWriter::NonImageClassesVisitor : public ClassVisitor {
  public:
   explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
 
@@ -890,9 +891,9 @@
     }
     ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
     for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
-      Class* klass = dex_cache->GetResolvedType(i);
+      Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
       if (klass != nullptr && !KeepClass(klass)) {
-        dex_cache->SetResolvedType(i, nullptr);
+        dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr);
       }
     }
     ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
@@ -1701,7 +1702,7 @@
   return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset);
 }
 
-class FixupRootVisitor : public RootVisitor {
+class ImageWriter::FixupRootVisitor : public RootVisitor {
  public:
   explicit FixupRootVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {
   }
@@ -1944,7 +1945,7 @@
 }
 
 // Rewrite all the references in the copied object to point to their image address equivalent
-class FixupVisitor {
+class ImageWriter::FixupVisitor {
  public:
   FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
   }
@@ -1980,7 +1981,7 @@
   mirror::Object* const copy_;
 };
 
-class FixupClassVisitor FINAL : public FixupVisitor {
+class ImageWriter::FixupClassVisitor FINAL : public FixupVisitor {
  public:
   FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) {
   }
@@ -2045,7 +2046,7 @@
   }
 }
 
-class NativeLocationVisitor {
+class ImageWriter::NativeLocationVisitor {
  public:
   explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
 
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index c9cf4cb..24fad46 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -582,14 +582,15 @@
   // Map of dex files to the indexes of oat files that they were compiled into.
   const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
 
-  friend class ContainsBootClassLoaderNonImageClassVisitor;
-  friend class FixupClassVisitor;
-  friend class FixupRootVisitor;
-  friend class FixupVisitor;
+  class ComputeLazyFieldsForClassesVisitor;
+  class FixupClassVisitor;
+  class FixupRootVisitor;
+  class FixupVisitor;
   class GetRootsVisitor;
-  friend class NativeLocationVisitor;
-  friend class NonImageClassesVisitor;
+  class NativeLocationVisitor;
+  class NonImageClassesVisitor;
   class VisitReferencesVisitor;
+
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
 };
 
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a9044a2..21042a3 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -20,6 +20,7 @@
 #include <math.h>
 
 #include "art_method-inl.h"
+#include "base/bit_utils.h"
 #include "class_linker.h"
 #include "common_compiler_test.h"
 #include "compiler.h"
@@ -366,7 +367,9 @@
   void StackArgsIntsFirstImpl();
   void StackArgsFloatsFirstImpl();
   void StackArgsMixedImpl();
+#if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
   void StackArgsSignExtendedMips64Impl();
+#endif
 
   void NormalNativeImpl();
   void FastNativeImpl();
@@ -2145,50 +2148,43 @@
 
 JNI_TEST_CRITICAL(StackArgsMixed)
 
-void Java_MyClassNatives_stackArgsSignExtendedMips64(JNIEnv*, jclass, jint i1, jint i2, jint i3,
-                                                     jint i4, jint i5, jint i6, jint i7, jint i8) {
-  EXPECT_EQ(i1, 1);
-  EXPECT_EQ(i2, 2);
-  EXPECT_EQ(i3, 3);
-  EXPECT_EQ(i4, 4);
-  EXPECT_EQ(i5, 5);
-  EXPECT_EQ(i6, 6);
-  EXPECT_EQ(i7, 7);
-  EXPECT_EQ(i8, -8);
-
 #if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
-  // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots.
-  // First 8 arguments are passed through registers, check i7 and i8.
-  uint32_t stack1_high = *(&i7 + 1);
-  uint32_t stack2_high = *(&i8 + 1);
-
-  EXPECT_EQ(stack1_high, static_cast<uint32_t>(0));
-  EXPECT_EQ(stack2_high, static_cast<uint32_t>(0xffffffff));
-#else
-  LOG(INFO) << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
-            << kRuntimeISA;
-  // Force-print to std::cout so it's also outside the logcat.
-  std::cout << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
-            << kRuntimeISA << std::endl;
-#endif
+// Function will fetch the last argument passed from caller that is now on top of the stack and
+// return it as a 8B long. That way we can test if the caller has properly sign-extended the
+// value when placing it on the stack.
+__attribute__((naked))
+jlong Java_MyClassNatives_getStackArgSignExtendedMips64(
+    JNIEnv*, jclass,                      // Arguments passed from caller
+    jint, jint, jint, jint, jint, jint,   // through regs a0 to a7.
+    jint) {                               // The last argument will be passed on the stack.
+  __asm__(
+      ".set noreorder\n\t"                // Just return and store 8 bytes from the top of the stack
+      "jr  $ra\n\t"                       // in v0 (in branch delay slot). This should be the last
+      "ld  $v0, 0($sp)\n\t");             // argument. It is a 32-bit int, but it should be sign
+                                          // extended and it occupies 64-bit location.
 }
 
 void JniCompilerTest::StackArgsSignExtendedMips64Impl() {
-  SetUpForTest(true, "stackArgsSignExtendedMips64", "(IIIIIIII)V",
-               CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsSignExtendedMips64));
-  jint i1 = 1;
-  jint i2 = 2;
-  jint i3 = 3;
-  jint i4 = 4;
-  jint i5 = 5;
-  jint i6 = 6;
-  jint i7 = 7;
-  jint i8 = -8;
+  uint64_t ret;
+  SetUpForTest(true,
+               "getStackArgSignExtendedMips64",
+               "(IIIIIII)J",
+               // Don't use wrapper because this is raw assembly function.
+               reinterpret_cast<void*>(&Java_MyClassNatives_getStackArgSignExtendedMips64));
 
-  env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8);
+  // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots.
+  // First 8 arguments are passed through registers.
+  // Final argument's value is 7. When sign-extended, higher stack bits should be 0.
+  ret = env_->CallStaticLongMethod(jklass_, jmethod_, 1, 2, 3, 4, 5, 6, 7);
+  EXPECT_EQ(High32Bits(ret), static_cast<uint32_t>(0));
+
+  // Final argument's value is -8.  When sign-extended, higher stack bits should be 0xffffffff.
+  ret = env_->CallStaticLongMethod(jklass_, jmethod_, 1, 2, 3, 4, 5, 6, -8);
+  EXPECT_EQ(High32Bits(ret), static_cast<uint32_t>(0xffffffff));
 }
 
-JNI_TEST_CRITICAL(StackArgsSignExtendedMips64)
+JNI_TEST(StackArgsSignExtendedMips64)
+#endif
 
 void Java_MyClassNatives_normalNative(JNIEnv*, jclass) {
   // Intentionally left empty.
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index 3fb7b56..33f4d77 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -222,7 +222,11 @@
                                                      bool is_synchronized,
                                                      bool is_critical_native,
                                                      const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, is_critical_native, shorty, kArm64PointerSize) {
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kArm64PointerSize) {
 }
 
 uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc
index 9859b5d..36a87a8 100644
--- a/compiler/jni/quick/calling_convention.cc
+++ b/compiler/jni/quick/calling_convention.cc
@@ -152,24 +152,6 @@
                                                                    bool is_critical_native,
                                                                    const char* shorty,
                                                                    InstructionSet instruction_set) {
-  if (UNLIKELY(is_critical_native)) {
-    // Sanity check that the requested JNI instruction set
-    // is supported for critical natives. Not every one is.
-    switch (instruction_set) {
-      case kX86_64:
-      case kX86:
-      case kArm64:
-      case kArm:
-      case kThumb2:
-        break;
-      default:
-        is_critical_native = false;
-        LOG(WARNING) << "@CriticalNative support not implemented for " << instruction_set
-                     << "; will crash at runtime if trying to invoke such a method.";
-        // TODO: implement for MIPS/MIPS64
-    }
-  }
-
   switch (instruction_set) {
 #ifdef ART_ENABLE_CODEGEN_arm
     case kArm:
@@ -191,12 +173,18 @@
 #ifdef ART_ENABLE_CODEGEN_mips
     case kMips:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) mips::MipsJniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) mips::MipsJniCallingConvention(is_static,
+                                                     is_synchronized,
+                                                     is_critical_native,
+                                                     shorty));
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips64
     case kMips64:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) mips64::Mips64JniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) mips64::Mips64JniCallingConvention(is_static,
+                                                         is_synchronized,
+                                                         is_critical_native,
+                                                         shorty));
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
     case kX86:
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index f541d8f..335a2df 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -370,14 +370,6 @@
     kObjectOrClass = 1
   };
 
-  // TODO: remove this constructor once all are changed to the below one.
-  JniCallingConvention(bool is_static,
-                       bool is_synchronized,
-                       const char* shorty,
-                       PointerSize frame_pointer_size)
-      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
-        is_critical_native_(false) {}
-
   JniCallingConvention(bool is_static,
                        bool is_synchronized,
                        bool is_critical_native,
diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc
index f5ab5f7..e6948ec 100644
--- a/compiler/jni/quick/mips/calling_convention_mips.cc
+++ b/compiler/jni/quick/mips/calling_convention_mips.cc
@@ -23,6 +23,13 @@
 namespace art {
 namespace mips {
 
+// Up to how many float-like (float, double) args can be enregistered in floating-point registers.
+// The rest of the args must go in integer registers or on the stack.
+constexpr size_t kMaxFloatOrDoubleRegisterArguments = 2u;
+// Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be
+// enregistered. The rest of the args must go on the stack.
+constexpr size_t kMaxIntLikeRegisterArguments = 4u;
+
 static const Register kCoreArgumentRegisters[] = { A0, A1, A2, A3 };
 static const FRegister kFArgumentRegisters[] = { F12, F14 };
 static const DRegister kDArgumentRegisters[] = { D6, D7 };
@@ -170,23 +177,134 @@
 }
 // JNI calling convention
 
-MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synchronized,
+MipsJniCallingConvention::MipsJniCallingConvention(bool is_static,
+                                                   bool is_synchronized,
+                                                   bool is_critical_native,
                                                    const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kMipsPointerSize) {
-  // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject
-  // or jclass for static methods and the JNIEnv. We start at the aligned register A2.
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kMipsPointerSize) {
+  // SYSTEM V - Application Binary Interface (MIPS RISC Processor):
+  // Data Representation - Fundamental Types (3-4) specifies fundamental alignments for each type.
+  //   "Each member is assigned to the lowest available offset with the appropriate alignment. This
+  // may require internal padding, depending on the previous member."
+  //
+  // All of our stack arguments are usually 4-byte aligned, however longs and doubles must be 8
+  // bytes aligned. Add padding to maintain 8-byte alignment invariant.
+  //
+  // Compute padding to ensure longs and doubles are not split in o32.
   size_t padding = 0;
-  for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) {
+  size_t cur_arg, cur_reg;
+  if (LIKELY(HasExtraArgumentsForJni())) {
+    // Ignore the 'this' jobject or jclass for static methods and the JNIEnv.
+    // We start at the aligned register A2.
+    //
+    // Ignore the first 2 parameters because they are guaranteed to be aligned.
+    cur_arg = NumImplicitArgs();  // Skip the "this" argument.
+    cur_reg = 2;  // Skip {A0=JNIEnv, A1=jobject} / {A0=JNIEnv, A1=jclass} parameters (start at A2).
+  } else {
+    // Check every parameter.
+    cur_arg = 0;
+    cur_reg = 0;
+  }
+
+  // Shift across a logical register mapping that looks like:
+  //
+  //   | A0 | A1 | A2 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 |
+  //
+  //   or some of variants with floating-point registers (F12 and F14), for example
+  //
+  //   | F12     | F14 | A3 | SP+16 | SP+20 | SP+24 | ... | SP+n | SP+n+4 |
+  //
+  //   (where SP is the stack pointer at the start of called function).
+  //
+  // Any time there would normally be a long/double in an odd logical register,
+  // we have to push out the rest of the mappings by 4 bytes to maintain an 8-byte alignment.
+  //
+  // This works for both physical register pairs {A0, A1}, {A2, A3},
+  // floating-point registers F12, F14 and for when the value is on the stack.
+  //
+  // For example:
+  // (a) long would normally go into A1, but we shift it into A2
+  //  | INT | (PAD) | LONG    |
+  //  | A0  |  A1   | A2 | A3 |
+  //
+  // (b) long would normally go into A3, but we shift it into SP
+  //  | INT | INT | INT | (PAD) | LONG        |
+  //  | A0  | A1  | A2  |  A3   | SP+16 SP+20 |
+  //
+  // where INT is any <=4 byte arg, and LONG is any 8-byte arg.
+  for (; cur_arg < NumArgs(); cur_arg++) {
     if (IsParamALongOrDouble(cur_arg)) {
       if ((cur_reg & 1) != 0) {
         padding += 4;
-        cur_reg++;  // additional bump to ensure alignment
+        cur_reg++;   // Additional bump to ensure alignment.
       }
-      cur_reg++;  // additional bump to skip extra long word
+      cur_reg += 2;  // Bump the iterator twice for every long argument.
+    } else {
+      cur_reg++;     // Bump the iterator for every argument.
     }
-    cur_reg++;  // bump the iterator for every argument
   }
-  padding_ = padding;
+  if (cur_reg < kMaxIntLikeRegisterArguments) {
+    // As a special case when, as a result of shifting (or not) there are no arguments on the stack,
+    // we actually have 0 stack padding.
+    //
+    // For example with @CriticalNative and:
+    // (int, long) -> shifts the long but doesn't need to pad the stack
+    //
+    //          shift
+    //           \/
+    //  | INT | (PAD) | LONG      | (EMPTY) ...
+    //  | r0  |  r1   |  r2  | r3 |   SP    ...
+    //                                /\
+    //                          no stack padding
+    padding_ = 0;
+  } else {
+    padding_ = padding;
+  }
+
+  // Argument Passing (3-17):
+  //   "When the first argument is integral, the remaining arguments are passed in the integer
+  // registers."
+  //
+  //   "The rules that determine which arguments go into registers and which ones must be passed on
+  // the stack are most easily explained by considering the list of arguments as a structure,
+  // aligned according to normal structure rules. Mapping of this structure into the combination of
+  // stack and registers is as follows: up to two leading floating-point arguments can be passed in
+  // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on
+  // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset.
+  // Holes left in the structure for alignment are unused, whether in registers or in the stack."
+  //
+  // For example with @CriticalNative and:
+  // (a) first argument is not floating-point, so all go into integer registers
+  //  | INT | FLOAT | DOUBLE  |
+  //  | A0  |  A1   | A2 | A3 |
+  // (b) first argument is floating-point, but 2nd is integer
+  //  | FLOAT | INT | DOUBLE  |
+  //  |  F12  | A1  | A2 | A3 |
+  // (c) first two arguments are floating-point (float, double)
+  //  | FLAOT | (PAD) | DOUBLE |  INT  |
+  //  |  F12  |       |  F14   | SP+16 |
+  // (d) first two arguments are floating-point (double, float)
+  //  | DOUBLE | FLOAT | INT |
+  //  |  F12   |  F14  | A3  |
+  // (e) first three arguments are floating-point, but just first two will go into fp registers
+  //  | DOUBLE | FLOAT | FLOAT |
+  //  |  F12   |  F14  |  A3   |
+  //
+  // Find out if the first argument is a floating-point. In that case, floating-point registers will
+  // be used for up to two leading floating-point arguments. Otherwise, all arguments will be passed
+  // using integer registers.
+  use_fp_arg_registers_ = false;
+  if (is_critical_native) {
+    if (NumArgs() > 0) {
+      if (IsParamAFloatOrDouble(0)) {
+        use_fp_arg_registers_ = true;
+      }
+    }
+  }
 }
 
 uint32_t MipsJniCallingConvention::CoreSpillMask() const {
@@ -202,74 +320,127 @@
 }
 
 size_t MipsJniCallingConvention::FrameSize() {
-  // ArtMethod*, RA and callee save area size, local reference segment state
-  size_t frame_data_size = static_cast<size_t>(kMipsPointerSize) +
-      (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
-  // References plus 2 words for HandleScope header
-  size_t handle_scope_size = HandleScope::SizeOf(kMipsPointerSize, ReferenceCount());
-  // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+  // ArtMethod*, RA and callee save area size, local reference segment state.
+  const size_t method_ptr_size = static_cast<size_t>(kMipsPointerSize);
+  const size_t ra_return_addr_size = kFramePointerSize;
+  const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
+
+  size_t frame_data_size = method_ptr_size + ra_return_addr_size + callee_save_area_size;
+
+  if (LIKELY(HasLocalReferenceSegmentState())) {
+    // Local reference segment state.
+    frame_data_size += kFramePointerSize;
+  }
+
+  // References plus 2 words for HandleScope header.
+  const size_t handle_scope_size = HandleScope::SizeOf(kMipsPointerSize, ReferenceCount());
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;    // Handle scope size.
+  }
+
+  // Plus return value spill area size.
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
 }
 
 size_t MipsJniCallingConvention::OutArgSize() {
-  return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, kStackAlignment);
+  // Argument Passing (3-17):
+  //   "Despite the fact that some or all of the arguments to a function are passed in registers,
+  // always allocate space on the stack for all arguments. This stack space should be a structure
+  // large enough to contain all the arguments, aligned according to normal structure rules (after
+  // promotion and structure return pointer insertion). The locations within the stack frame used
+  // for arguments are called the home locations."
+  //
+  // Allocate 16 bytes for home locations + space needed for stack arguments.
+  return RoundUp(
+      (kMaxIntLikeRegisterArguments + NumberOfOutgoingStackArgs()) * kFramePointerSize + padding_,
+      kStackAlignment);
 }
 
 ArrayRef<const ManagedRegister> MipsJniCallingConvention::CalleeSaveRegisters() const {
   return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
 }
 
-// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
-// in even register numbers and stack slots
+// JniCallingConvention ABI follows o32 where longs and doubles must occur
+// in even register numbers and stack slots.
 void MipsJniCallingConvention::Next() {
   JniCallingConvention::Next();
-  size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-  if ((itr_args_ >= 2) &&
-      (arg_pos < NumArgs()) &&
-      IsParamALongOrDouble(arg_pos)) {
-    // itr_slots_ needs to be an even number, according to AAPCS.
-    if ((itr_slots_ & 0x1u) != 0) {
+
+  if (LIKELY(HasNext())) {  // Avoid CHECK failure for IsCurrentParam
+    // Ensure slot is 8-byte aligned for longs/doubles (o32).
+    if (IsCurrentParamALongOrDouble() && ((itr_slots_ & 0x1u) != 0)) {
+      // itr_slots_ needs to be an even number, according to o32.
       itr_slots_++;
     }
   }
 }
 
 bool MipsJniCallingConvention::IsCurrentParamInRegister() {
-  return itr_slots_ < 4;
+  // Argument Passing (3-17):
+  //   "The rules that determine which arguments go into registers and which ones must be passed on
+  // the stack are most easily explained by considering the list of arguments as a structure,
+  // aligned according to normal structure rules. Mapping of this structure into the combination of
+  // stack and registers is as follows: up to two leading floating-point arguments can be passed in
+  // $f12 and $f14; everything else with a structure offset greater than or equal to 16 is passed on
+  // the stack. The remainder of the arguments are passed in $4..$7 based on their structure offset.
+  // Holes left in the structure for alignment are unused, whether in registers or in the stack."
+  //
+  // Even when floating-point registers are used, there can be up to 4 arguments passed in
+  // registers.
+  return itr_slots_ < kMaxIntLikeRegisterArguments;
 }
 
 bool MipsJniCallingConvention::IsCurrentParamOnStack() {
   return !IsCurrentParamInRegister();
 }
 
-static const Register kJniArgumentRegisters[] = {
-  A0, A1, A2, A3
-};
 ManagedRegister MipsJniCallingConvention::CurrentParamRegister() {
-  CHECK_LT(itr_slots_, 4u);
-  int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-  if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) {
-    CHECK_EQ(itr_slots_, 2u);
-    return MipsManagedRegister::FromRegisterPair(A2_A3);
+  CHECK_LT(itr_slots_, kMaxIntLikeRegisterArguments);
+  // Up to two leading floating-point arguments can be passed in floating-point registers.
+  if (use_fp_arg_registers_ && (itr_args_ < kMaxFloatOrDoubleRegisterArguments)) {
+    if (IsCurrentParamAFloatOrDouble()) {
+      if (IsCurrentParamADouble()) {
+        return MipsManagedRegister::FromDRegister(kDArgumentRegisters[itr_args_]);
+      } else {
+        return MipsManagedRegister::FromFRegister(kFArgumentRegisters[itr_args_]);
+      }
+    }
+  }
+  // All other arguments (including other floating-point arguments) will be passed in integer
+  // registers.
+  if (IsCurrentParamALongOrDouble()) {
+    if (itr_slots_ == 0u) {
+      return MipsManagedRegister::FromRegisterPair(A0_A1);
+    } else {
+      CHECK_EQ(itr_slots_, 2u);
+      return MipsManagedRegister::FromRegisterPair(A2_A3);
+    }
   } else {
-    return
-      MipsManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
+    return MipsManagedRegister::FromCoreRegister(kCoreArgumentRegisters[itr_slots_]);
   }
 }
 
 FrameOffset MipsJniCallingConvention::CurrentParamStackOffset() {
-  CHECK_GE(itr_slots_, 4u);
+  CHECK_GE(itr_slots_, kMaxIntLikeRegisterArguments);
   size_t offset = displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize);
   CHECK_LT(offset, OutArgSize());
   return FrameOffset(offset);
 }
 
 size_t MipsJniCallingConvention::NumberOfOutgoingStackArgs() {
-  size_t static_args = IsStatic() ? 1 : 0;  // count jclass
-  // regular argument parameters and this
-  size_t param_args = NumArgs() + NumLongOrDoubleArgs();
-  // count JNIEnv*
-  return static_args + param_args + 1;
+  size_t static_args = HasSelfClass() ? 1 : 0;            // Count jclass.
+  // Regular argument parameters and this.
+  size_t param_args = NumArgs() + NumLongOrDoubleArgs();  // Twice count 8-byte args.
+  // Count JNIEnv* less arguments in registers.
+  size_t internal_args = (HasJniEnv() ? 1 : 0);
+  size_t total_args = static_args + param_args + internal_args;
+
+  return total_args - std::min(kMaxIntLikeRegisterArguments, static_cast<size_t>(total_args));
 }
+
 }  // namespace mips
 }  // namespace art
diff --git a/compiler/jni/quick/mips/calling_convention_mips.h b/compiler/jni/quick/mips/calling_convention_mips.h
index e95a738..ad3f118 100644
--- a/compiler/jni/quick/mips/calling_convention_mips.h
+++ b/compiler/jni/quick/mips/calling_convention_mips.h
@@ -54,14 +54,17 @@
 
 class MipsJniCallingConvention FINAL : public JniCallingConvention {
  public:
-  MipsJniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  MipsJniCallingConvention(bool is_static,
+                           bool is_synchronized,
+                           bool is_critical_native,
+                           const char* shorty);
   ~MipsJniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
   ManagedRegister IntReturnRegister() OVERRIDE;
   ManagedRegister InterproceduralScratchRegister() OVERRIDE;
   // JNI calling convention
-  void Next() OVERRIDE;  // Override default behavior for AAPCS
+  void Next() OVERRIDE;  // Override default behavior for o32.
   size_t FrameSize() OVERRIDE;
   size_t OutArgSize() OVERRIDE;
   ArrayRef<const ManagedRegister> CalleeSaveRegisters() const OVERRIDE;
@@ -82,8 +85,9 @@
   size_t NumberOfOutgoingStackArgs() OVERRIDE;
 
  private:
-  // Padding to ensure longs and doubles are not split in AAPCS
+  // Padding to ensure longs and doubles are not split in o32.
   size_t padding_;
+  size_t use_fp_arg_registers_;
 
   DISALLOW_COPY_AND_ASSIGN(MipsJniCallingConvention);
 };
diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc
index 8341e8e..afe6a76 100644
--- a/compiler/jni/quick/mips64/calling_convention_mips64.cc
+++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc
@@ -23,6 +23,9 @@
 namespace art {
 namespace mips64 {
 
+// Up to kow many args can be enregistered. The rest of the args must go on the stack.
+constexpr size_t kMaxRegisterArguments = 8u;
+
 static const GpuRegister kGpuArgumentRegisters[] = {
   A0, A1, A2, A3, A4, A5, A6, A7
 };
@@ -150,9 +153,15 @@
 
 // JNI calling convention
 
-Mips64JniCallingConvention::Mips64JniCallingConvention(bool is_static, bool is_synchronized,
+Mips64JniCallingConvention::Mips64JniCallingConvention(bool is_static,
+                                                       bool is_synchronized,
+                                                       bool is_critical_native,
                                                        const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kMips64PointerSize) {
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kMips64PointerSize) {
 }
 
 uint32_t Mips64JniCallingConvention::CoreSpillMask() const {
@@ -168,13 +177,28 @@
 }
 
 size_t Mips64JniCallingConvention::FrameSize() {
-  // ArtMethod*, RA and callee save area size, local reference segment state
-  size_t frame_data_size = kFramePointerSize +
-      (CalleeSaveRegisters().size() + 1) * kFramePointerSize + sizeof(uint32_t);
-  // References plus 2 words for HandleScope header
+  // ArtMethod*, RA and callee save area size, local reference segment state.
+  size_t method_ptr_size = static_cast<size_t>(kFramePointerSize);
+  size_t ra_and_callee_save_area_size = (CalleeSaveRegisters().size() + 1) * kFramePointerSize;
+
+  size_t frame_data_size = method_ptr_size + ra_and_callee_save_area_size;
+  if (LIKELY(HasLocalReferenceSegmentState())) {                     // Local ref. segment state.
+    // Local reference segment state is sometimes excluded.
+    frame_data_size += sizeof(uint32_t);
+  }
+  // References plus 2 words for HandleScope header.
   size_t handle_scope_size = HandleScope::SizeOf(kMips64PointerSize, ReferenceCount());
-  // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;                                 // Handle scope size.
+  }
+
+  // Plus return value spill area size.
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
 }
 
 size_t Mips64JniCallingConvention::OutArgSize() {
@@ -186,7 +210,7 @@
 }
 
 bool Mips64JniCallingConvention::IsCurrentParamInRegister() {
-  return itr_args_ < 8;
+  return itr_args_ < kMaxRegisterArguments;
 }
 
 bool Mips64JniCallingConvention::IsCurrentParamOnStack() {
@@ -204,7 +228,8 @@
 
 FrameOffset Mips64JniCallingConvention::CurrentParamStackOffset() {
   CHECK(IsCurrentParamOnStack());
-  size_t offset = displacement_.Int32Value() - OutArgSize() + ((itr_args_ - 8) * kFramePointerSize);
+  size_t args_on_stack = itr_args_ - kMaxRegisterArguments;
+  size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize);
   CHECK_LT(offset, OutArgSize());
   return FrameOffset(offset);
 }
@@ -214,7 +239,7 @@
   size_t all_args = NumArgs() + NumberOfExtraArgumentsForJni();
 
   // Nothing on the stack unless there are more than 8 arguments
-  return (all_args > 8) ? all_args - 8 : 0;
+  return (all_args > kMaxRegisterArguments) ? all_args - kMaxRegisterArguments : 0;
 }
 }  // namespace mips64
 }  // namespace art
diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.h b/compiler/jni/quick/mips64/calling_convention_mips64.h
index a5fd111..faedaef 100644
--- a/compiler/jni/quick/mips64/calling_convention_mips64.h
+++ b/compiler/jni/quick/mips64/calling_convention_mips64.h
@@ -54,7 +54,10 @@
 
 class Mips64JniCallingConvention FINAL : public JniCallingConvention {
  public:
-  Mips64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  Mips64JniCallingConvention(bool is_static,
+                             bool is_synchronized,
+                             bool is_critical_native,
+                             const char* shorty);
   ~Mips64JniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 102637f..9458576 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -125,7 +125,9 @@
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify) {
     TimingLogger timings("WriteElf", false, false);
-    OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
+    OatWriter oat_writer(/*compiling_boot_image*/false,
+                         &timings,
+                         /*profile_compilation_info*/nullptr);
     for (const DexFile* dex_file : dex_files) {
       ArrayRef<const uint8_t> raw_dex_file(
           reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
@@ -145,7 +147,9 @@
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify) {
     TimingLogger timings("WriteElf", false, false);
-    OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
+    OatWriter oat_writer(/*compiling_boot_image*/false,
+                         &timings,
+                         /*profile_compilation_info*/nullptr);
     for (const char* dex_filename : dex_filenames) {
       if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) {
         return false;
@@ -161,7 +165,9 @@
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify) {
     TimingLogger timings("WriteElf", false, false);
-    OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
+    OatWriter oat_writer(/*compiling_boot_image*/false,
+                         &timings,
+                         /*profile_compilation_info*/nullptr);
     if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
       return false;
     }
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f9173f5..153aff4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -33,6 +33,7 @@
 #include "debug/method_debug_info.h"
 #include "dex/verification_results.h"
 #include "dex_file-inl.h"
+#include "dexlayout.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "gc/space/image_space.h"
@@ -260,16 +261,7 @@
   // Data to write to a separate section.
   dchecked_vector<uint32_t> class_offsets_;
 
-  void InitTypeLookupTable(const DexFile& dex_file, uint8_t* storage) const {
-    lookup_table_.reset(TypeLookupTable::Create(dex_file, storage));
-  }
-
-  TypeLookupTable* GetTypeLookupTable() const {
-    return lookup_table_.get();
-  }
-
  private:
-  mutable std::unique_ptr<TypeLookupTable> lookup_table_;
   size_t GetClassOffsetsRawSize() const {
     return class_offsets_.size() * sizeof(class_offsets_[0]);
   }
@@ -285,7 +277,7 @@
   DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
     << "file_offset=" << file_offset << " offset_=" << offset_
 
-OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings)
+OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info)
   : write_state_(WriteState::kAddingDexFileSources),
     timings_(timings),
     raw_dex_files_(),
@@ -346,7 +338,8 @@
     size_oat_class_method_bitmaps_(0),
     size_oat_class_method_offsets_(0),
     relative_patcher_(nullptr),
-    absolute_patch_locations_() {
+    absolute_patch_locations_(),
+    profile_compilation_info_(info) {
 }
 
 bool OatWriter::AddDexFileSource(const char* filename,
@@ -410,6 +403,35 @@
   return true;
 }
 
+// Add dex file source(s) from a vdex file specified by a file handle.
+bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file,
+                                      const char* location,
+                                      CreateTypeLookupTable create_type_lookup_table) {
+  DCHECK(write_state_ == WriteState::kAddingDexFileSources);
+  const uint8_t* current_dex_data = nullptr;
+  for (size_t i = 0; ; ++i) {
+    current_dex_data = vdex_file.GetNextDexFileData(current_dex_data);
+    if (current_dex_data == nullptr) {
+      break;
+    }
+    if (!DexFile::IsMagicValid(current_dex_data)) {
+      LOG(ERROR) << "Invalid magic in vdex file created from " << location;
+      return false;
+    }
+    // We used `zipped_dex_file_locations_` to keep the strings in memory.
+    zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location));
+    const char* full_location = zipped_dex_file_locations_.back().c_str();
+    oat_dex_files_.emplace_back(full_location,
+                                DexFileSource(current_dex_data),
+                                create_type_lookup_table);
+  }
+  if (oat_dex_files_.empty()) {
+    LOG(ERROR) << "No dex files in vdex file created from " << location;
+    return false;
+  }
+  return true;
+}
+
 // Add dex file source from raw memory.
 bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data,
                                     const char* location,
@@ -2090,7 +2112,11 @@
   if (!SeekToDexFile(out, file, oat_dex_file)) {
     return false;
   }
-  if (oat_dex_file->source_.IsZipEntry()) {
+  if (profile_compilation_info_ != nullptr) {
+    if (!LayoutAndWriteDexFile(out, oat_dex_file)) {
+      return false;
+    }
+  } else if (oat_dex_file->source_.IsZipEntry()) {
     if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
       return false;
     }
@@ -2155,6 +2181,39 @@
   return true;
 }
 
+bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) {
+  TimingLogger::ScopedTiming split("Dex Layout", timings_);
+  std::string error_msg;
+  std::string location(oat_dex_file->GetLocation());
+  std::unique_ptr<const DexFile> dex_file;
+  if (oat_dex_file->source_.IsZipEntry()) {
+    ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry();
+    std::unique_ptr<MemMap> mem_map(
+        zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg));
+    dex_file = DexFile::Open(location,
+                             zip_entry->GetCrc32(),
+                             std::move(mem_map),
+                             /* verify */ true,
+                             /* verify_checksum */ true,
+                             &error_msg);
+  } else {
+    DCHECK(oat_dex_file->source_.IsRawFile());
+    File* raw_file = oat_dex_file->source_.GetRawFile();
+    dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg);
+  }
+  Options options;
+  options.output_to_memmap_ = true;
+  DexLayout dex_layout(options, profile_compilation_info_, nullptr);
+  dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0);
+  std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap());
+  if (!WriteDexFile(out, oat_dex_file, mem_map->Begin())) {
+    return false;
+  }
+  // Set the checksum of the new oat dex file to be the original file's checksum.
+  oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum();
+  return true;
+}
+
 bool OatWriter::WriteDexFile(OutputStream* out,
                              File* file,
                              OatDexFile* oat_dex_file,
@@ -2481,8 +2540,15 @@
 
     // Create the lookup table. When `nullptr` is given as the storage buffer,
     // TypeLookupTable allocates its own and OatDexFile takes ownership.
-    oat_dex_file->InitTypeLookupTable(*opened_dex_files[i], /* storage */ nullptr);
-    TypeLookupTable* table = oat_dex_file->GetTypeLookupTable();
+    const DexFile& dex_file = *opened_dex_files[i];
+    {
+      std::unique_ptr<TypeLookupTable> type_lookup_table =
+          TypeLookupTable::Create(dex_file, /* storage */ nullptr);
+      type_lookup_table_oat_dex_files_.push_back(
+          std::make_unique<art::OatDexFile>(std::move(type_lookup_table)));
+      dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get());
+    }
+    TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable();
 
     // Type tables are required to be 4 byte aligned.
     size_t initial_offset = oat_size_;
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 3d08ad3..0dcf79e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -38,9 +38,11 @@
 class CompiledMethod;
 class CompilerDriver;
 class ImageWriter;
+class ProfileCompilationInfo;
 class OutputStream;
 class TimingLogger;
 class TypeLookupTable;
+class VdexFile;
 class ZipEntry;
 
 namespace debug {
@@ -110,12 +112,13 @@
     kDefault = kCreate
   };
 
-  OatWriter(bool compiling_boot_image, TimingLogger* timings);
+  OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info);
 
   // To produce a valid oat file, the user must first add sources with any combination of
   //   - AddDexFileSource(),
   //   - AddZippedDexFilesSource(),
-  //   - AddRawDexFileSource().
+  //   - AddRawDexFileSource(),
+  //   - AddVdexDexFilesSource().
   // Then the user must call in order
   //   - WriteAndOpenDexFiles()
   //   - Initialize()
@@ -144,6 +147,11 @@
       const char* location,
       uint32_t location_checksum,
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
+  // Add dex file source(s) from a vdex file.
+  bool AddVdexDexFilesSource(
+      const VdexFile& vdex_file,
+      const char* location,
+      CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
   dchecked_vector<const char*> GetSourceLocations() const;
 
   // Write raw dex files to the vdex file, mmap the file and open the dex files from it.
@@ -258,6 +266,7 @@
   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);
+  bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file);
   bool WriteDexFile(OutputStream* out,
                     File* file,
                     OatDexFile* oat_dex_file,
@@ -363,6 +372,9 @@
   // Offset of the oat data from the start of the mmapped region of the elf file.
   size_t oat_data_offset_;
 
+  // Fake OatDexFiles to hold type lookup tables for the compiler.
+  std::vector<std::unique_ptr<art::OatDexFile>> type_lookup_table_oat_dex_files_;
+
   // data to write
   std::unique_ptr<OatHeader> oat_header_;
   dchecked_vector<OatDexFile> oat_dex_files_;
@@ -419,6 +431,9 @@
   // The locations of absolute patches relative to the start of the executable section.
   dchecked_vector<uintptr_t> absolute_patch_locations_;
 
+  // Profile info used to generate new layout of files.
+  ProfileCompilationInfo* profile_compilation_info_;
+
   DISALLOW_COPY_AND_ASSIGN(OatWriter);
 };
 
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index b7c24ff..dfa1504 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -70,9 +70,9 @@
   graph_->AddBlock(entry);
   graph_->SetEntryBlock(entry);
   HInstruction* parameter1 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);  // array
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);  // array
   HInstruction* parameter2 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);  // i
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);  // i
   entry->AddInstruction(parameter1);
   entry->AddInstruction(parameter2);
 
@@ -167,9 +167,9 @@
   graph_->AddBlock(entry);
   graph_->SetEntryBlock(entry);
   HInstruction* parameter1 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);  // array
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);  // array
   HInstruction* parameter2 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);  // i
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);  // i
   entry->AddInstruction(parameter1);
   entry->AddInstruction(parameter2);
 
@@ -231,9 +231,9 @@
   graph_->AddBlock(entry);
   graph_->SetEntryBlock(entry);
   HInstruction* parameter1 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);  // array
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);  // array
   HInstruction* parameter2 = new (&allocator_)
-      HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);  // i
+      HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);  // i
   entry->AddInstruction(parameter1);
   entry->AddInstruction(parameter2);
 
@@ -295,7 +295,7 @@
   graph_->AddBlock(entry);
   graph_->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator_) HParameterValue(
-      graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HInstruction* constant_5 = graph_->GetIntConstant(5);
@@ -364,7 +364,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HInstruction* constant_initial = graph->GetIntConstant(initial);
@@ -479,7 +479,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HInstruction* constant_initial = graph->GetIntConstant(initial);
@@ -600,7 +600,7 @@
       constant_10,
       graph->GetCurrentMethod(),
       0,
-      Primitive::kPrimInt,
+      dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)),
       graph->GetDexFile(),
       kQuickAllocArray);
   block->AddInstruction(new_array);
@@ -692,7 +692,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HInstruction* constant_initial = graph->GetIntConstant(initial);
@@ -795,7 +795,7 @@
   graph_->AddBlock(entry);
   graph_->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator_) HParameterValue(
-      graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HInstruction* constant_0 = graph_->GetIntConstant(0);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 8b450e1..9f6b78a 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1375,4 +1375,33 @@
   return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
 }
 
+void CodeGenerator::EmitJitRoots(uint8_t* code,
+                                 Handle<mirror::ObjectArray<mirror::Object>> roots,
+                                 const uint8_t* roots_data,
+                                 Handle<mirror::DexCache> outer_dex_cache) {
+  DCHECK_EQ(static_cast<size_t>(roots->GetLength()), GetNumberOfJitRoots());
+  StackHandleScope<1> hs(Thread::Current());
+  MutableHandle<mirror::DexCache> h_dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  size_t index = 0;
+  for (auto& entry : jit_string_roots_) {
+    const DexFile& entry_dex_file = *entry.first.dex_file;
+    // Avoid the expensive FindDexCache call by checking if the string is
+    // in the compiled method's dex file.
+    h_dex_cache.Assign(IsSameDexFile(*outer_dex_cache->GetDexFile(), entry_dex_file)
+        ? outer_dex_cache.Get()
+        : class_linker->FindDexCache(hs.Self(), entry_dex_file));
+    mirror::String* string = class_linker->LookupString(
+        entry_dex_file, entry.first.string_index, h_dex_cache);
+    DCHECK(string != nullptr) << "JIT roots require strings to have been loaded";
+    // Ensure the string is strongly interned. This is a requirement on how the JIT
+    // handles strings. b/32995596
+    class_linker->GetInternTable()->InternStrong(string);
+    roots->Set(index, string);
+    entry.second = index;
+    ++index;
+  }
+  EmitJitRootPatches(code, roots_data);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index bf246ad..a5d19ab 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -32,6 +32,7 @@
 #include "optimizing_compiler_stats.h"
 #include "read_barrier_option.h"
 #include "stack_map_stream.h"
+#include "string_reference.h"
 #include "utils/label.h"
 
 namespace art {
@@ -335,6 +336,17 @@
 
   void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item);
   size_t ComputeStackMapsSize();
+  size_t GetNumberOfJitRoots() const {
+    return jit_string_roots_.size();
+  }
+
+  // Fills the `literals` array with literals collected during code generation.
+  // Also emits literal patches.
+  void EmitJitRoots(uint8_t* code,
+                    Handle<mirror::ObjectArray<mirror::Object>> roots,
+                    const uint8_t* roots_data,
+                    Handle<mirror::DexCache> outer_dex_cache)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsLeafMethod() const {
     return is_leaf_;
@@ -515,6 +527,26 @@
   virtual HLoadClass::LoadKind GetSupportedLoadClassKind(
       HLoadClass::LoadKind desired_class_load_kind) = 0;
 
+  static LocationSummary::CallKind GetLoadStringCallKind(HLoadString* load) {
+    switch (load->GetLoadKind()) {
+      case HLoadString::LoadKind::kBssEntry:
+        DCHECK(load->NeedsEnvironment());
+        return LocationSummary::kCallOnSlowPath;
+      case HLoadString::LoadKind::kDexCacheViaMethod:
+        DCHECK(load->NeedsEnvironment());
+        return LocationSummary::kCallOnMainOnly;
+      case HLoadString::LoadKind::kJitTableAddress:
+        DCHECK(!load->NeedsEnvironment());
+        return kEmitCompilerReadBarrier
+            ? LocationSummary::kCallOnSlowPath
+            : LocationSummary::kNoCall;
+        break;
+      default:
+        DCHECK(!load->NeedsEnvironment());
+        return LocationSummary::kNoCall;
+    }
+  }
+
   // Check if the desired_dispatch_info is supported. If it is, return it,
   // otherwise return a fall-back info that should be used instead.
   virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -571,6 +603,8 @@
         fpu_callee_save_mask_(fpu_callee_save_mask),
         stack_map_stream_(graph->GetArena()),
         block_order_(nullptr),
+        jit_string_roots_(StringReferenceValueComparator(),
+                          graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         disasm_info_(nullptr),
         stats_(stats),
         graph_(graph),
@@ -637,6 +671,12 @@
     return current_slow_path_;
   }
 
+  // Emit the patches assocatied with JIT roots. Only applies to JIT compiled code.
+  virtual void EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED,
+                                  const uint8_t* roots_data ATTRIBUTE_UNUSED) {
+    DCHECK_EQ(jit_string_roots_.size(), 0u);
+  }
+
   // Frame size required for this method.
   uint32_t frame_size_;
   uint32_t core_spill_mask_;
@@ -662,6 +702,11 @@
   // The order to use for code generation.
   const ArenaVector<HBasicBlock*>* block_order_;
 
+  // Maps a StringReference (dex_file, string_index) to the index in the literal table.
+  // Entries are intially added with a 0 index, and `EmitJitRoots` will compute all the
+  // indices.
+  ArenaSafeMap<StringReference, size_t, StringReferenceValueComparator> jit_string_roots_;
+
   DisassemblyInformation* disasm_info_;
 
  private:
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index c6363d1..8a6b94e 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -383,7 +383,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
+    __ LoadImmediate(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
                                                 : kQuickInitializeType;
     arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
@@ -1214,7 +1214,9 @@
                                graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_address_patches_(std::less<uint32_t>(),
-                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_string_patches_(StringReferenceValueComparator(),
+                          graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   // Always save the LR register to mimic Quick.
   AddAllocatedRegister(Location::RegisterLocation(LR));
 }
@@ -3951,7 +3953,7 @@
 
 void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+  __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
@@ -5741,7 +5743,7 @@
 void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -5828,7 +5830,7 @@
                         current_method,
                         ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
+      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
       GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option);
       generate_null_check = !cls->IsInDexCache();
     }
@@ -5893,6 +5895,9 @@
     case HLoadString::LoadKind::kBssEntry:
       DCHECK(!Runtime::Current()->UseJitCompilation());
       break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
   }
@@ -5900,13 +5905,8 @@
 }
 
 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
-      ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
-          ? LocationSummary::kCallOnMainOnly
-          : LocationSummary::kCallOnSlowPath)
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetOut(Location::RegisterLocation(R0));
@@ -5979,6 +5979,13 @@
       __ Bind(slow_path->GetExitLabel());
       return;
     }
+    case HLoadString::LoadKind::kJitTableAddress: {
+      __ LoadLiteral(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
+                                                                load->GetStringIndex()));
+      // /* GcRoot<mirror::String> */ out = *out
+      GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+      return;
+    }
     default:
       break;
   }
@@ -6490,12 +6497,9 @@
                                         iftable_offset,
                                         maybe_temp2_loc,
                                         kWithoutReadBarrier);
-      // Null iftable means it is empty and will always fail the check.
-      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
-
-      // Loop through the iftable and check if any class matches.
+      // Iftable is never null.
       __ ldr(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset));
-
+      // Loop through the iftable and check if any class matches.
       Label start_loop;
       __ Bind(&start_loop);
       __ CompareAndBranchIfZero(maybe_temp2_loc.AsRegister<Register>(),
@@ -7320,8 +7324,8 @@
 }
 
 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch(
-    const DexFile& dex_file, uint32_t type_index) {
-  return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_);
+    const DexFile& dex_file, dex::TypeIndex type_index) {
+  return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
 }
 
 CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch(
@@ -7343,7 +7347,7 @@
 }
 
 Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
-                                                           uint32_t type_index) {
+                                                           dex::TypeIndex type_index) {
   return boot_image_type_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
@@ -7359,6 +7363,14 @@
   return DeduplicateUint32Literal(address, &uint32_literals_);
 }
 
+Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                       uint32_t string_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+  return jit_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
 template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
 inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches(
     const ArenaDeque<PcRelativePatchInfo>& infos,
@@ -7440,7 +7452,7 @@
     uint32_t literal_offset = literal->GetLabel()->Position();
     linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
                                                      target_type.dex_file,
-                                                     target_type.type_index));
+                                                     target_type.type_index.index_));
   }
   EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
                                                               linker_patches);
@@ -7675,6 +7687,21 @@
   }
 }
 
+void CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const auto& entry : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(entry.first);
+    DCHECK(it != jit_string_roots_.end());
+    size_t index_in_table = it->second;
+    Literal* literal = entry.second;
+    DCHECK(literal->GetLabel()->IsBound());
+    uint32_t literal_offset = literal->GetLabel()->Position();
+    uintptr_t address =
+        reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+    uint8_t* data = code + literal_offset;
+    reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
+  }
+}
+
 #undef __
 #undef QUICK_ENTRY_POINT
 
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index f95dd57..a4ccb57 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -19,6 +19,7 @@
 
 #include "base/enums.h"
 #include "code_generator.h"
+#include "dex_file_types.h"
 #include "driver/compiler_options.h"
 #include "nodes.h"
 #include "string_reference.h"
@@ -481,16 +482,19 @@
   };
 
   PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
-  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index);
+  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
   PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
                                                        uint32_t element_offset);
   Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index);
-  Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index);
+  Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
   Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
   Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
+  Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, uint32_t string_index);
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference field load when Baker's read barriers are used.
   void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -591,9 +595,9 @@
 
   using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
   using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
-  using BootStringToLiteralMap = ArenaSafeMap<StringReference,
-                                              Literal*,
-                                              StringReferenceValueComparator>;
+  using StringToLiteralMap = ArenaSafeMap<StringReference,
+                                          Literal*,
+                                          StringReferenceValueComparator>;
   using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
                                             Literal*,
                                             TypeReferenceValueComparator>;
@@ -605,7 +609,6 @@
   PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
                                           uint32_t offset_or_index,
                                           ArenaDeque<PcRelativePatchInfo>* patches);
-
   template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
   static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
                                           ArenaVector<LinkerPatch>* linker_patches);
@@ -630,7 +633,7 @@
   // PC-relative patch info for each HArmDexCacheArraysBase.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
   // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
-  BootStringToLiteralMap boot_image_string_patches_;
+  StringToLiteralMap boot_image_string_patches_;
   // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
   ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
   // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
@@ -640,6 +643,9 @@
   // Deduplication map for patchable boot image addresses.
   Uint32ToLiteralMap boot_image_address_patches_;
 
+  // Patches for string literals in JIT compiled code.
+  StringToLiteralMap jit_string_patches_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
 };
 
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 4ab6065..a78b3da 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -288,7 +288,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex());
+    __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex().index_);
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
                                                 : kQuickInitializeType;
     arm64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
@@ -572,8 +572,10 @@
 
   // We are about to use the assembler to place literals directly. Make sure we have enough
   // underlying code buffer and we have generated the jump table with right size.
-  CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), num_entries * sizeof(int32_t),
-                             CodeBufferCheckScope::kCheck, CodeBufferCheckScope::kExactSize);
+  vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(),
+                                   num_entries * sizeof(int32_t),
+                                   vixl::CodeBufferCheckScope::kReserveBufferSpace,
+                                   vixl::CodeBufferCheckScope::kExactSize);
 
   __ Bind(&table_start_);
   const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
@@ -1158,7 +1160,9 @@
                                graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       boot_image_address_patches_(std::less<uint32_t>(),
-                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+                                  graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_string_patches_(StringReferenceValueComparator(),
+                          graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   // Save the link register (containing the return address) to mimic Quick.
   AddAllocatedRegister(LocationFrom(lr));
 }
@@ -2258,10 +2262,10 @@
         masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize;
     if (prev->IsLoadOrStore()) {
       // Make sure we emit only exactly one nop.
-      vixl::aarch64::CodeBufferCheckScope scope(masm,
-                                                kInstructionSize,
-                                                vixl::aarch64::CodeBufferCheckScope::kCheck,
-                                                vixl::aarch64::CodeBufferCheckScope::kExactSize);
+      vixl::CodeBufferCheckScope scope(masm,
+                                       kInstructionSize,
+                                       vixl::CodeBufferCheckScope::kReserveBufferSpace,
+                                       vixl::CodeBufferCheckScope::kExactSize);
       __ nop();
     }
   }
@@ -3802,12 +3806,9 @@
                                         iftable_offset,
                                         maybe_temp2_loc,
                                         kWithoutReadBarrier);
-      // Null iftable means it is empty and will always fail the check.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
-
-      // Loop through the iftable and check if any class matches.
+      // Iftable is never null.
       __ Ldr(WRegisterFrom(maybe_temp2_loc), HeapOperand(temp.W(), array_length_offset));
-
+      // Loop through the iftable and check if any class matches.
       vixl::aarch64::Label start_loop;
       __ Bind(&start_loop);
       __ Cbz(WRegisterFrom(maybe_temp2_loc), type_check_slow_path->GetEntryLabel());
@@ -4037,7 +4038,8 @@
       vixl::aarch64::Label* label = &relative_call_patches_.back().label;
       SingleEmissionCheckScope guard(GetVIXLAssembler());
       __ Bind(label);
-      __ bl(0);  // Branch and link to itself. This will be overriden at link time.
+      // Branch and link to itself. This will be overriden at link time.
+      __ bl(static_cast<int64_t>(0));
       break;
     }
     case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
@@ -4103,9 +4105,9 @@
 
 vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeTypePatch(
     const DexFile& dex_file,
-    uint32_t type_index,
+    dex::TypeIndex type_index,
     vixl::aarch64::Label* adrp_label) {
-  return NewPcRelativePatch(dex_file, type_index, adrp_label, &pc_relative_type_patches_);
+  return NewPcRelativePatch(dex_file, type_index.index_, adrp_label, &pc_relative_type_patches_);
 }
 
 vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch(
@@ -4137,7 +4139,7 @@
 }
 
 vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageTypeLiteral(
-    const DexFile& dex_file, uint32_t type_index) {
+    const DexFile& dex_file, dex::TypeIndex type_index) {
   return boot_image_type_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
@@ -4155,12 +4157,20 @@
   return DeduplicateUint64Literal(address);
 }
 
+vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral(
+    const DexFile& dex_file, uint32_t string_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u);
+  return jit_string_patches_.GetOrCreate(
+      StringReference(&dex_file, string_index),
+      [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
+}
+
 void CodeGeneratorARM64::EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label,
                                              vixl::aarch64::Register reg) {
   DCHECK(reg.IsX());
   SingleEmissionCheckScope guard(GetVIXLAssembler());
   __ Bind(fixup_label);
-  __ adrp(reg, /* offset placeholder */ 0);
+  __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0));
 }
 
 void CodeGeneratorARM64::EmitAddPlaceholder(vixl::aarch64::Label* fixup_label,
@@ -4250,7 +4260,7 @@
     vixl::aarch64::Literal<uint32_t>* literal = entry.second;
     linker_patches->push_back(LinkerPatch::TypePatch(literal->GetOffset(),
                                                      target_type.dex_file,
-                                                     target_type.type_index));
+                                                     target_type.type_index.index_));
   }
   EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
                                                                 linker_patches);
@@ -4374,7 +4384,7 @@
 
 void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -4410,7 +4420,7 @@
       DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
       // Add ADRP with its PC-relative type patch.
       const DexFile& dex_file = cls->GetDexFile();
-      uint32_t type_index = cls->GetTypeIndex();
+      dex::TypeIndex type_index = cls->GetTypeIndex();
       vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index);
       codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
       // Add ADD with its PC-relative type patch.
@@ -4478,7 +4488,7 @@
       GenerateGcRootFieldLoad(cls,
                               out_loc,
                               out.X(),
-                              CodeGenerator::GetCacheOffset(cls->GetTypeIndex()),
+                              CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_),
                               /* fixup_label */ nullptr,
                               read_barrier_option);
       generate_null_check = !cls->IsInDexCache();
@@ -4540,16 +4550,15 @@
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      break;
   }
   return desired_string_load_kind;
 }
 
 void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
-      ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
-          ? LocationSummary::kCallOnMainOnly
-          : LocationSummary::kCallOnSlowPath)
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
     InvokeRuntimeCallingConvention calling_convention;
@@ -4575,6 +4584,7 @@
 
 void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
   Register out = OutputRegister(load);
+  Location out_loc = load->GetLocations()->Out();
 
   switch (load->GetLoadKind()) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
@@ -4611,9 +4621,9 @@
       // Add LDR with its PC-relative String patch.
       vixl::aarch64::Label* ldr_label =
           codegen_->NewPcRelativeStringPatch(dex_file, string_index, adrp_label);
-      // /* GcRoot<mirror::Class> */ out = *(base_address + offset)  /* PC-relative */
+      // /* GcRoot<mirror::String> */ out = *(base_address + offset)  /* PC-relative */
       GenerateGcRootFieldLoad(load,
-                              load->GetLocations()->Out(),
+                              out_loc,
                               temp,
                               /* offset placeholder */ 0u,
                               ldr_label,
@@ -4625,6 +4635,17 @@
       __ Bind(slow_path->GetExitLabel());
       return;
     }
+    case HLoadString::LoadKind::kJitTableAddress: {
+      __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
+                                                        load->GetStringIndex()));
+      GenerateGcRootFieldLoad(load,
+                              out_loc,
+                              out.X(),
+                              /* offset */ 0,
+                              /* fixup_label */ nullptr,
+                              kCompilerReadBarrierOption);
+      return;
+    }
     default:
       break;
   }
@@ -4757,7 +4778,7 @@
   InvokeRuntimeCallingConvention calling_convention;
   Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
   DCHECK(type_index.Is(w0));
-  __ Mov(type_index, instruction->GetTypeIndex());
+  __ Mov(type_index, instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
@@ -5744,7 +5765,19 @@
   }
 }
 
-
+void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const auto& entry : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(entry.first);
+    DCHECK(it != jit_string_roots_.end());
+    size_t index_in_table = it->second;
+    vixl::aarch64::Literal<uint32_t>* literal = entry.second;
+    uint32_t literal_offset = literal->GetOffset();
+    uintptr_t address =
+        reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+    uint8_t* data = code + literal_offset;
+    reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
+  }
+}
 
 #undef __
 #undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 0e8d4fd..1545fd3 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -20,6 +20,7 @@
 #include "arch/arm64/quick_method_frame_info_arm64.h"
 #include "code_generator.h"
 #include "common_arm64.h"
+#include "dex_file_types.h"
 #include "driver/compiler_options.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
@@ -547,7 +548,7 @@
   // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
   // to the associated ADRP patch label).
   vixl::aarch64::Label* NewPcRelativeTypePatch(const DexFile& dex_file,
-                                               uint32_t type_index,
+                                               dex::TypeIndex type_index,
                                                vixl::aarch64::Label* adrp_label = nullptr);
 
   // Add a new PC-relative dex cache array patch for an instruction and return
@@ -562,9 +563,11 @@
   vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
                                                                       uint32_t string_index);
   vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
-                                                                    uint32_t type_index);
+                                                                    dex::TypeIndex type_index);
   vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address);
   vixl::aarch64::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address);
+  vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file,
+                                                                uint32_t string_index);
 
   void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg);
   void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label,
@@ -576,6 +579,8 @@
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference field load when Baker's read barriers are used.
   void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -673,9 +678,9 @@
   using MethodToLiteralMap = ArenaSafeMap<MethodReference,
                                           vixl::aarch64::Literal<uint64_t>*,
                                           MethodReferenceComparator>;
-  using BootStringToLiteralMap = ArenaSafeMap<StringReference,
-                                              vixl::aarch64::Literal<uint32_t>*,
-                                              StringReferenceValueComparator>;
+  using StringToLiteralMap = ArenaSafeMap<StringReference,
+                                          vixl::aarch64::Literal<uint32_t>*,
+                                          StringReferenceValueComparator>;
   using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
                                             vixl::aarch64::Literal<uint32_t>*,
                                             TypeReferenceValueComparator>;
@@ -739,7 +744,7 @@
   // PC-relative DexCache access info.
   ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
   // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
-  BootStringToLiteralMap boot_image_string_patches_;
+  StringToLiteralMap boot_image_string_patches_;
   // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
   ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
   // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
@@ -749,6 +754,9 @@
   // Deduplication map for patchable boot image addresses.
   Uint32ToLiteralMap boot_image_address_patches_;
 
+  // Patches for string literals in JIT compiled code.
+  StringToLiteralMap jit_string_patches_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
 };
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index e9827e8..e399f32 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -47,6 +47,7 @@
 using helpers::InputRegisterAt;
 using helpers::InputSRegisterAt;
 using helpers::InputVRegisterAt;
+using helpers::Int32ConstantFrom;
 using helpers::LocationFrom;
 using helpers::LowRegisterFrom;
 using helpers::LowSRegisterFrom;
@@ -132,7 +133,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        __ Add(base, sp, stack_offset);
+        __ Add(base, sp, Operand::From(stack_offset));
       }
       __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
     }
@@ -180,7 +181,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        __ Add(base, sp, stack_offset);
+        __ Add(base, sp, Operand::From(stack_offset));
       }
       __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
     }
@@ -398,7 +399,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConventionARMVIXL calling_convention;
-    __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
+    __ Mov(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
                                                 : kQuickInitializeType;
     arm_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
@@ -673,13 +674,22 @@
   DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
 
   // We are about to use the assembler to place literals directly. Make sure we have enough
-  // underlying code buffer and we have generated the jump table with right size.
-  codegen->GetVIXLAssembler()->GetBuffer().Align();
+  // underlying code buffer and we have generated a jump table of the right size, using
+  // codegen->GetVIXLAssembler()->GetBuffer().Align();
   AssemblerAccurateScope aas(codegen->GetVIXLAssembler(),
                              num_entries * sizeof(int32_t),
                              CodeBufferCheckScope::kMaximumSize);
   // TODO(VIXL): Check that using lower case bind is fine here.
   codegen->GetVIXLAssembler()->bind(&table_start_);
+  for (uint32_t i = 0; i < num_entries; i++) {
+    codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
+  }
+}
+
+void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
+  uint32_t num_entries = switch_instr_->GetNumEntries();
+  DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
+
   const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
   for (uint32_t i = 0; i < num_entries; i++) {
     vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
@@ -691,21 +701,21 @@
     }
     DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
     DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
-    vixl32::Literal<int32_t> literal(jump_offset);
-    codegen->GetVIXLAssembler()->place(&literal);
+
+    bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
   }
 }
 
-void CodeGeneratorARMVIXL::EmitJumpTables() {
+void CodeGeneratorARMVIXL::FixJumpTables() {
   for (auto&& jump_table : jump_tables_) {
-    jump_table->EmitTable(this);
+    jump_table->FixTable(this);
   }
 }
 
 #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->  // NOLINT
 
 void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
-  EmitJumpTables();
+  FixJumpTables();
   GetAssembler()->FinalizeCode();
   CodeGenerator::Finalize(allocator);
 }
@@ -1143,7 +1153,8 @@
 void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
                                                             size_t condition_input_index,
                                                             vixl32::Label* true_target,
-                                                            vixl32::Label* false_target) {
+                                                            vixl32::Label* false_target,
+                                                            bool far_target) {
   HInstruction* cond = instruction->InputAt(condition_input_index);
 
   if (true_target == nullptr && false_target == nullptr) {
@@ -1179,9 +1190,13 @@
       DCHECK(cond_val.IsRegister());
     }
     if (true_target == nullptr) {
-      __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
+      __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
+                                false_target,
+                                far_target);
     } else {
-      __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
+      __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
+                                   true_target,
+                                   far_target);
     }
   } else {
     // Condition has not been materialized. Use its inputs as the comparison and
@@ -1276,7 +1291,8 @@
   GenerateTestAndBranch(select,
                         /* condition_input_index */ 2,
                         /* true_target */ nullptr,
-                        &false_target);
+                        &false_target,
+                        /* far_target */ false);
   codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
   __ Bind(&false_target);
 }
@@ -1652,7 +1668,20 @@
   // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
   // instruction from clobbering it as they might use r12 as a scratch register.
   DCHECK(hidden_reg.Is(r12));
-  __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+
+  {
+    // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
+    // so it checks if the application is using them (by passing them to the macro assembler
+    // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
+    // what is available, and is the opposite of the standard usage: Instead of requesting a
+    // temporary location, it imposes an external constraint (i.e. a specific register is reserved
+    // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
+    // (to materialize the constant), since the destination register becomes available for such use
+    // internally for the duration of the macro instruction.
+    UseScratchRegisterScope temps(GetVIXLAssembler());
+    temps.Exclude(hidden_reg);
+    __ Mov(hidden_reg, invoke->GetDexMethodIndex());
+  }
 
   {
     AssemblerAccurateScope aas(GetVIXLAssembler(),
@@ -2016,7 +2045,7 @@
         case Primitive::kPrimFloat: {
           // Processing a Dex `float-to-int' instruction.
           vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
-          __ Vcvt(I32, F32, temp, InputSRegisterAt(conversion, 0));
+          __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
           __ Vmov(OutputRegister(conversion), temp);
           break;
         }
@@ -2024,7 +2053,7 @@
         case Primitive::kPrimDouble: {
           // Processing a Dex `double-to-int' instruction.
           vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
-          __ Vcvt(I32, F64, temp_s, DRegisterFrom(in));
+          __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
           __ Vmov(OutputRegister(conversion), temp_s);
           break;
         }
@@ -2100,7 +2129,7 @@
         case Primitive::kPrimChar: {
           // Processing a Dex `int-to-float' instruction.
           __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
-          __ Vcvt(F32, I32, OutputSRegister(conversion), OutputSRegister(conversion));
+          __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
           break;
         }
 
@@ -2131,7 +2160,7 @@
         case Primitive::kPrimChar: {
           // Processing a Dex `int-to-double' instruction.
           __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
-          __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out));
+          __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
           break;
         }
 
@@ -2139,18 +2168,15 @@
           // Processing a Dex `long-to-double' instruction.
           vixl32::Register low = LowRegisterFrom(in);
           vixl32::Register high = HighRegisterFrom(in);
-
           vixl32::SRegister out_s = LowSRegisterFrom(out);
           vixl32::DRegister out_d = DRegisterFrom(out);
-
           vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
           vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
-
-          vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0));
+          vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
 
           // temp_d = int-to-double(high)
           __ Vmov(temp_s, high);
-          __ Vcvt(F64, I32, temp_d, temp_s);
+          __ Vcvt(F64, S32, temp_d, temp_s);
           // constant_d = k2Pow32EncodingForDouble
           __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
           // out_d = unsigned-to-double(low)
@@ -2446,13 +2472,13 @@
   vixl32::Register dividend = InputRegisterAt(instruction, 0);
   vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
   vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
-  int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  int32_t imm = Int32ConstantFrom(second);
 
   int64_t magic;
   int shift;
   CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
 
-  __ Mov(temp1, magic);
+  __ Mov(temp1, Operand::From(magic));
   __ Smull(temp2, temp1, dividend, temp1);
 
   if (imm > 0 && magic < 0) {
@@ -2769,7 +2795,7 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt: {
       if (value.IsRegister()) {
-        __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
       } else {
         DCHECK(value.IsConstant()) << value;
         if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
@@ -2845,9 +2871,9 @@
     }
     // Rotate, or mov to out for zero or word size rotations.
     if (rot != 0u) {
-      __ Lsr(out_reg_hi, in_reg_hi, rot);
+      __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
       __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
-      __ Lsr(out_reg_lo, in_reg_lo, rot);
+      __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
       __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
     } else {
       __ Mov(out_reg_lo, in_reg_lo);
@@ -2862,7 +2888,7 @@
     __ And(shift_right, RegisterFrom(rhs), 0x1F);
     __ Lsrs(shift_left, RegisterFrom(rhs), 6);
     // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
-    __ Rsb(shift_left, shift_right, kArmBitsPerWord);
+    __ Rsb(shift_left, shift_right, Operand::From(kArmBitsPerWord));
     __ B(cc, &shift_by_32_plus_shift_right);
 
     // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
@@ -3028,11 +3054,11 @@
           // Shift the high part
           __ Lsl(o_h, high, o_l);
           // Shift the low part and `or` what overflew on the high part
-          __ Rsb(temp, o_l, kArmBitsPerWord);
+          __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
           __ Lsr(temp, low, temp);
           __ Orr(o_h, o_h, temp);
           // If the shift is > 32 bits, override the high part
-          __ Subs(temp, o_l, kArmBitsPerWord);
+          __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3047,11 +3073,11 @@
           // Shift the low part
           __ Lsr(o_l, low, o_h);
           // Shift the high part and `or` what underflew on the low part
-          __ Rsb(temp, o_h, kArmBitsPerWord);
+          __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
           __ Lsl(temp, high, temp);
           __ Orr(o_l, o_l, temp);
           // If the shift is > 32 bits, override the low part
-          __ Subs(temp, o_h, kArmBitsPerWord);
+          __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3065,10 +3091,10 @@
           __ And(o_h, second_reg, kMaxLongShiftDistance);
           // same as Shr except we use `Lsr`s and not `Asr`s
           __ Lsr(o_l, low, o_h);
-          __ Rsb(temp, o_h, kArmBitsPerWord);
+          __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
           __ Lsl(temp, high, temp);
           __ Orr(o_l, o_l, temp);
-          __ Subs(temp, o_h, kArmBitsPerWord);
+          __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
           {
             AssemblerAccurateScope guard(GetVIXLAssembler(),
                                          3 * kArmInstrMaxSizeInBytes,
@@ -3211,7 +3237,7 @@
 
 void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConventionARMVIXL calling_convention;
-  __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+  __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
@@ -3412,7 +3438,7 @@
     __ Add(temp, addr, offset);
     addr = temp;
   }
-  __ Ldrexd(out_lo, out_hi, addr);
+  __ Ldrexd(out_lo, out_hi, MemOperand(addr));
 }
 
 void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
@@ -3432,10 +3458,10 @@
   __ Bind(&fail);
   // We need a load followed by store. (The address used in a STREX instruction must
   // be the same as the address in the most recently executed LDREX instruction.)
-  __ Ldrexd(temp1, temp2, addr);
+  __ Ldrexd(temp1, temp2, MemOperand(addr));
   codegen_->MaybeRecordImplicitNullCheck(instruction);
-  __ Strexd(temp1, value_lo, value_hi, addr);
-  __ Cbnz(temp1, &fail);
+  __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
+  __ CompareAndBranchIfNonZero(temp1, &fail);
 }
 
 void LocationsBuilderARMVIXL::HandleFieldSet(
@@ -3951,7 +3977,7 @@
   NullCheckSlowPathARMVIXL* slow_path =
       new (GetGraph()->GetArena()) NullCheckSlowPathARMVIXL(instruction);
   AddSlowPath(slow_path);
-  __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+  __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
 }
 
 void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
@@ -4164,7 +4190,14 @@
         vixl32::Register temp = temps.Acquire();
 
         if (has_intermediate_address) {
-          TODO_VIXL32(FATAL);
+          // We do not need to compute the intermediate address from the array: the
+          // input instruction has done it already. See the comment in
+          // `TryExtractArrayAccessAddress()`.
+          if (kIsDebugBuild) {
+            HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
+            DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+          }
+          temp = obj;
         } else {
           __ Add(temp, obj, data_offset);
         }
@@ -4209,7 +4242,14 @@
           vixl32::Register temp = temps.Acquire();
 
           if (has_intermediate_address) {
-            TODO_VIXL32(FATAL);
+            // We do not need to compute the intermediate address from the array: the
+            // input instruction has done it already. See the comment in
+            // `TryExtractArrayAccessAddress()`.
+            if (kIsDebugBuild) {
+              HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
+              DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+            }
+            temp = obj;
           } else {
             __ Add(temp, obj, data_offset);
           }
@@ -4337,7 +4377,14 @@
         vixl32::Register temp = temps.Acquire();
 
         if (has_intermediate_address) {
-          TODO_VIXL32(FATAL);
+          // We do not need to compute the intermediate address from the array: the
+          // input instruction has done it already. See the comment in
+          // `TryExtractArrayAccessAddress()`.
+          if (kIsDebugBuild) {
+            HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
+            DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
+          }
+          temp = array;
         } else {
           __ Add(temp, array, data_offset);
         }
@@ -4386,7 +4433,7 @@
         codegen_->AddSlowPath(slow_path);
         if (instruction->GetValueCanBeNull()) {
           vixl32::Label non_zero;
-          __ Cbnz(value, &non_zero);
+          __ CompareAndBranchIfNonZero(value, &non_zero);
           if (index.IsConstant()) {
             size_t offset =
                (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4434,7 +4481,7 @@
           GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
           // If heap poisoning is enabled, no need to unpoison
           // `temp1`, as we are comparing against null below.
-          __ Cbnz(temp1, slow_path->GetEntryLabel());
+          __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
           __ Bind(&do_put);
         } else {
           __ B(ne, slow_path->GetEntryLabel());
@@ -4556,6 +4603,32 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
+  // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+  DCHECK(!kEmitCompilerReadBarrier);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
+  vixl32::Register out = OutputRegister(instruction);
+  vixl32::Register first = InputRegisterAt(instruction, 0);
+  Location second = instruction->GetLocations()->InAt(1);
+
+  // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+  DCHECK(!kEmitCompilerReadBarrier);
+
+  if (second.IsRegister()) {
+    __ Add(out, first, RegisterFrom(second));
+  } else {
+    __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
+  }
+}
+
 void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
   RegisterSet caller_saves = RegisterSet::Empty();
   InvokeRuntimeCallingConventionARMVIXL calling_convention;
@@ -4585,11 +4658,11 @@
                                       bool can_be_null) {
   vixl32::Label is_null;
   if (can_be_null) {
-    __ Cbz(value, &is_null);
+    __ CompareAndBranchIfZero(value, &is_null);
   }
   GetAssembler()->LoadFromOffset(
       kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
-  __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+  __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
   __ Strb(card, MemOperand(card, temp));
   if (can_be_null) {
     __ Bind(&is_null);
@@ -4644,10 +4717,10 @@
   GetAssembler()->LoadFromOffset(
       kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
   if (successor == nullptr) {
-    __ Cbnz(temp, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
     __ Bind(slow_path->GetReturnLabel());
   } else {
-    __ Cbz(temp, codegen_->GetLabelOf(successor));
+    __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
     __ B(slow_path->GetEntryLabel());
   }
 }
@@ -4915,7 +4988,7 @@
 void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -4947,7 +5020,7 @@
           ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value();
       GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
+      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
       GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
       generate_null_check = !cls->IsInDexCache();
       break;
@@ -4962,7 +5035,7 @@
         cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
     codegen_->AddSlowPath(slow_path);
     if (generate_null_check) {
-      __ Cbz(out, slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
     }
     if (cls->MustGenerateClinitCheck()) {
       GenerateClassInitializationCheck(slow_path, out);
@@ -5153,14 +5226,17 @@
   // Return 0 if `obj` is null.
   // avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ Cbz(obj, &zero);
+    __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false);
   }
 
-  // /* HeapReference<Class> */ out = obj->klass_
-  GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc);
-
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck: {
+      // /* HeapReference<Class> */ out = obj->klass_
+      GenerateReferenceLoadTwoRegisters(instruction,
+                                        out_loc,
+                                        obj_loc,
+                                        class_offset,
+                                        maybe_temp_loc);
       __ Cmp(out, cls);
       // Classes must be equal for the instanceof to succeed.
       __ B(ne, &zero);
@@ -5170,6 +5246,12 @@
     }
 
     case TypeCheckKind::kAbstractClassCheck: {
+      // /* HeapReference<Class> */ out = obj->klass_
+      GenerateReferenceLoadTwoRegisters(instruction,
+                                        out_loc,
+                                        obj_loc,
+                                        class_offset,
+                                        maybe_temp_loc);
       // If the class is abstract, we eagerly fetch the super class of the
       // object to avoid doing a comparison we know will fail.
       vixl32::Label loop;
@@ -5177,7 +5259,7 @@
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ Cbz(out, &done);
+      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
       __ Cmp(out, cls);
       __ B(ne, &loop);
       __ Mov(out, 1);
@@ -5188,6 +5270,12 @@
     }
 
     case TypeCheckKind::kClassHierarchyCheck: {
+      // /* HeapReference<Class> */ out = obj->klass_
+      GenerateReferenceLoadTwoRegisters(instruction,
+                                        out_loc,
+                                        obj_loc,
+                                        class_offset,
+                                        maybe_temp_loc);
       // Walk over the class hierarchy to find a match.
       vixl32::Label loop, success;
       __ Bind(&loop);
@@ -5195,7 +5283,7 @@
       __ B(eq, &success);
       // /* HeapReference<Class> */ out = out->super_class_
       GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc);
-      __ Cbnz(out, &loop);
+      __ CompareAndBranchIfNonZero(out, &loop);
       // If `out` is null, we use it for the result, and jump to `done`.
       __ B(&done);
       __ Bind(&success);
@@ -5207,6 +5295,12 @@
     }
 
     case TypeCheckKind::kArrayObjectCheck: {
+      // /* HeapReference<Class> */ out = obj->klass_
+      GenerateReferenceLoadTwoRegisters(instruction,
+                                        out_loc,
+                                        obj_loc,
+                                        class_offset,
+                                        maybe_temp_loc);
       // Do an exact check.
       vixl32::Label exact_check;
       __ Cmp(out, cls);
@@ -5215,10 +5309,10 @@
       // /* HeapReference<Class> */ out = out->component_type_
       GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc);
       // If `out` is null, we use it for the result, and jump to `done`.
-      __ Cbz(out, &done);
+      __ CompareAndBranchIfZero(out, &done, /* far_target */ false);
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-      __ Cbnz(out, &zero);
+      __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false);
       __ Bind(&exact_check);
       __ Mov(out, 1);
       __ B(&done);
@@ -5226,6 +5320,12 @@
     }
 
     case TypeCheckKind::kArrayCheck: {
+      // /* HeapReference<Class> */ out = obj->klass_
+      GenerateReferenceLoadTwoRegisters(instruction,
+                                        out_loc,
+                                        obj_loc,
+                                        class_offset,
+                                        maybe_temp_loc);
       __ Cmp(out, cls);
       DCHECK(locations->OnlyCallsOnSlowPath());
       slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction,
@@ -5348,7 +5448,7 @@
   vixl32::Label done;
   // Avoid null check if we know obj is not null.
   if (instruction->MustDoNullCheck()) {
-    __ Cbz(obj, &done);
+    __ CompareAndBranchIfZero(obj, &done, /* far_target */ false);
   }
 
   // /* HeapReference<Class> */ temp = obj->klass_
@@ -5374,7 +5474,7 @@
 
       // If the class reference currently in `temp` is null, jump to the slow path to throw the
       // exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
 
       // Otherwise, compare the classes.
       __ Cmp(temp, cls);
@@ -5394,7 +5494,7 @@
 
       // If the class reference currently in `temp` is null, jump to the slow path to throw the
       // exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
       // Otherwise, jump to the beginning of the loop.
       __ B(&loop);
       break;
@@ -5409,12 +5509,12 @@
       // /* HeapReference<Class> */ temp = temp->component_type_
       GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc);
       // If the component type is null, jump to the slow path to throw the exception.
-      __ Cbz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
       // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
       // to further check that this component type is not a primitive type.
       GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
       static_assert(Primitive::kPrimNot == 0, "Expected 0 for art::Primitive::kPrimNot");
-      __ Cbnz(temp, type_check_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
       break;
     }
 
@@ -5491,6 +5591,70 @@
   HandleBitwiseOperation(instruction);
 }
 
+void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  DCHECK(instruction->GetResultType() == Primitive::kPrimInt
+         || instruction->GetResultType() == Primitive::kPrimLong);
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  Location out = locations->Out();
+
+  if (instruction->GetResultType() == Primitive::kPrimInt) {
+    vixl32::Register first_reg = RegisterFrom(first);
+    vixl32::Register second_reg = RegisterFrom(second);
+    vixl32::Register out_reg = RegisterFrom(out);
+
+    switch (instruction->GetOpKind()) {
+      case HInstruction::kAnd:
+        __ Bic(out_reg, first_reg, second_reg);
+        break;
+      case HInstruction::kOr:
+        __ Orn(out_reg, first_reg, second_reg);
+        break;
+      // There is no EON on arm.
+      case HInstruction::kXor:
+      default:
+        LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
+        UNREACHABLE();
+    }
+    return;
+
+  } else {
+    DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+    vixl32::Register first_low = LowRegisterFrom(first);
+    vixl32::Register first_high = HighRegisterFrom(first);
+    vixl32::Register second_low = LowRegisterFrom(second);
+    vixl32::Register second_high = HighRegisterFrom(second);
+    vixl32::Register out_low = LowRegisterFrom(out);
+    vixl32::Register out_high = HighRegisterFrom(out);
+
+    switch (instruction->GetOpKind()) {
+      case HInstruction::kAnd:
+        __ Bic(out_low, first_low, second_low);
+        __ Bic(out_high, first_high, second_high);
+        break;
+      case HInstruction::kOr:
+        __ Orn(out_low, first_low, second_low);
+        __ Orn(out_high, first_high, second_high);
+        break;
+      // There is no EON on arm.
+      case HInstruction::kXor:
+      default:
+        LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
+        UNREACHABLE();
+    }
+  }
+}
+
 // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
 void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
                                                        vixl32::Register first,
@@ -5858,6 +6022,32 @@
   __ Blx(lr);
 }
 
+void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
+  locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
+                     Location::RequiresRegister());
+  locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
+  locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+  vixl32::Register res = OutputRegister(instr);
+  vixl32::Register accumulator =
+      InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
+  vixl32::Register mul_left =
+      InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
+  vixl32::Register mul_right =
+      InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
+
+  if (instr->GetOpKind() == HInstruction::kAdd) {
+    __ Mla(res, mul_left, mul_right, accumulator);
+  } else {
+    __ Mls(res, mul_left, mul_right, accumulator);
+  }
+}
+
 void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
   // Nothing to do, this should be removed during prepare for register allocator.
   LOG(FATAL) << "Unreachable";
@@ -5952,6 +6142,8 @@
     vixl32::Register target_address = table_base;
     __ Add(target_address, table_base, jump_offset);
     __ Bx(target_address);
+
+    jump_table->EmitTable(codegen_);
   }
 }
 
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index ccd866c..38c756f 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -112,6 +112,7 @@
   M(ArraySet)                                   \
   M(Below)                                      \
   M(BelowOrEqual)                               \
+  M(BitwiseNegatedRight)                        \
   M(BooleanNot)                                 \
   M(BoundsCheck)                                \
   M(BoundType)                                  \
@@ -136,6 +137,7 @@
   M(InstanceFieldSet)                           \
   M(InstanceOf)                                 \
   M(IntConstant)                                \
+  M(IntermediateAddress)                        \
   M(InvokeInterface)                            \
   M(InvokeStaticOrDirect)                       \
   M(InvokeUnresolved)                           \
@@ -149,6 +151,7 @@
   M(MemoryBarrier)                              \
   M(MonitorOperation)                           \
   M(Mul)                                        \
+  M(MultiplyAccumulate)                         \
   M(NativeDebugInfo)                            \
   M(Neg)                                        \
   M(NewArray)                                   \
@@ -186,24 +189,33 @@
 // TODO: Remove once the VIXL32 backend is implemented completely.
 #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)   \
   M(ArmDexCacheArraysBase)                      \
-  M(BitwiseNegatedRight)                        \
-  M(IntermediateAddress)                        \
-  M(MultiplyAccumulate)                         \
 
 class CodeGeneratorARMVIXL;
 
 class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
  public:
+  typedef vixl::aarch32::Literal<int32_t> IntLiteral;
+
   explicit JumpTableARMVIXL(HPackedSwitch* switch_instr)
-      : switch_instr_(switch_instr), table_start_() {}
+      : switch_instr_(switch_instr),
+        table_start_(),
+        bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+    uint32_t num_entries = switch_instr_->GetNumEntries();
+    for (uint32_t i = 0; i < num_entries; i++) {
+      IntLiteral *lit = new IntLiteral(0);
+      bb_addresses_.emplace_back(lit);
+    }
+  }
 
   vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; }
 
   void EmitTable(CodeGeneratorARMVIXL* codegen);
+  void FixTable(CodeGeneratorARMVIXL* codegen);
 
  private:
   HPackedSwitch* const switch_instr_;
   vixl::aarch32::Label table_start_;
+  ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_;
 
   DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL);
 };
@@ -357,7 +369,7 @@
   FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
 
   ArmVIXLAssembler* GetAssembler() const { return assembler_; }
-  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
  private:
   void VisitUnimplemementedInstruction(HInstruction* instruction) {
@@ -439,7 +451,8 @@
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              vixl::aarch32::Label* true_target,
-                             vixl::aarch32::Label* false_target);
+                             vixl::aarch32::Label* false_target,
+                             bool far_target = true);
   void GenerateCompareTestAndBranch(HCondition* condition,
                                     vixl::aarch32::Label* true_target,
                                     vixl::aarch32::Label* false_target);
@@ -492,7 +505,7 @@
 
   const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
 
-  vixl::aarch32::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
 
   size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
 
@@ -513,7 +526,7 @@
 
   HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
 
-  void EmitJumpTables();
+  void FixJumpTables();
   void GenerateMemoryBarrier(MemBarrierKind kind);
   void Finalize(CodeAllocator* allocator) OVERRIDE;
   void SetupBlockedRegisters() const OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 573bb50..8f94834 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -224,7 +224,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
+    __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
 
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
                                                 : kQuickInitializeType;
@@ -1056,7 +1056,7 @@
     uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
     linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
                                                      target_type.dex_file,
-                                                     target_type.type_index));
+                                                     target_type.type_index.index_));
   }
   for (const auto& entry : boot_image_address_patches_) {
     DCHECK(GetCompilerOptions().GetIncludePatchInformation());
@@ -1073,8 +1073,8 @@
 }
 
 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch(
-    const DexFile& dex_file, uint32_t type_index) {
-  return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_);
+    const DexFile& dex_file, dex::TypeIndex type_index) {
+  return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
 }
 
 CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeDexCacheArrayPatch(
@@ -1117,7 +1117,7 @@
 }
 
 Literal* CodeGeneratorMIPS::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
-                                                            uint32_t type_index) {
+                                                            dex::TypeIndex type_index) {
   return boot_image_type_patches_.GetOrCreate(
       TypeReference(&dex_file, type_index),
       [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
@@ -5207,6 +5207,11 @@
     case HLoadString::LoadKind::kDexCacheViaMethod:
       fallback_load = false;
       break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      // TODO: implement.
+      fallback_load = true;
+      break;
   }
   if (fallback_load) {
     desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
@@ -5534,7 +5539,7 @@
 void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -5628,7 +5633,7 @@
                         base_or_current_method_reg,
                         ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
+      size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
       GenerateGcRootFieldLoad(cls, out_loc, out, offset);
       generate_null_check = !cls->IsInDexCache();
     }
@@ -5970,7 +5975,7 @@
   Register current_method_register = calling_convention.GetRegisterAt(2);
   __ Lw(current_method_register, SP, kCurrentMethodStackOffset);
   // Move an uint16_t value to a register.
-  __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+  __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
                        void*, uint32_t, int32_t, ArtMethod*>();
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index e132819..e225d20 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -449,11 +449,11 @@
   };
 
   PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
-  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index);
+  PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
   PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
                                                        uint32_t element_offset);
   Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index);
-  Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index);
+  Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
   Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
 
   void EmitPcRelativeAddressPlaceholder(PcRelativePatchInfo* info, Register out, Register base);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 1a54935..02b01c8 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -180,7 +180,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex());
+    __ LoadConst32(calling_convention.GetRegisterAt(0), cls_->GetTypeIndex().index_);
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
                                                 : kQuickInitializeType;
     mips64_codegen->InvokeRuntime(entrypoint, at_, dex_pc_, this);
@@ -3157,7 +3157,7 @@
 void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -3174,7 +3174,7 @@
     __ LoadFromOffset(kLoadDoubleword, out, current_method,
                       ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
     __ LoadFromOffset(
-        kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+        kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_));
     // TODO: We will need a read barrier here.
     if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
       DCHECK(cls->CanCallRuntime());
@@ -3382,7 +3382,8 @@
 void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   // Move an uint16_t value to a register.
-  __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(), instruction->GetTypeIndex());
+  __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(),
+                 instruction->GetTypeIndex().index_);
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
   CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
 }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 2451b8d..51e902a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -265,7 +265,7 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex()));
+    __ movl(calling_convention.GetRegisterAt(0), Immediate(cls_->GetTypeIndex().index_));
     x86_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage
                                           : kQuickInitializeType,
                                at_, dex_pc_, this);
@@ -1012,6 +1012,7 @@
       simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       constant_area_start_(-1),
       fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       method_address_offset_(-1) {
@@ -4167,7 +4168,7 @@
 
 void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
+  __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_));
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
@@ -4611,7 +4612,7 @@
 }
 
 void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) {
-  type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex());
+  type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_);
   __ Bind(&type_patches_.back().label);
 }
 
@@ -6059,7 +6060,7 @@
 void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -6141,7 +6142,8 @@
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       GenerateGcRootFieldLoad(cls,
                               out_loc,
-                              Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+                              Address(out,
+                                      CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)),
                               /* fixup_label */ nullptr,
                               read_barrier_option);
       generate_null_check = !cls->IsInDexCache();
@@ -6218,16 +6220,15 @@
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      break;
   }
   return desired_string_load_kind;
 }
 
 void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
-          ? LocationSummary::kCallOnMainOnly
-          : LocationSummary::kCallOnSlowPath)
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
@@ -6252,6 +6253,14 @@
   }
 }
 
+Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u);
+  // Add a patch entry and return the label.
+  jit_string_patches_.emplace_back(dex_file, dex_index);
+  PatchInfo<Label>* info = &jit_string_patches_.back();
+  return &info->label;
+}
+
 void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
@@ -6280,7 +6289,7 @@
       Register method_address = locations->InAt(0).AsRegister<Register>();
       Address address = Address(method_address, CodeGeneratorX86::kDummy32BitOffset);
       Label* fixup_label = codegen_->NewStringBssEntryPatch(load);
-      // /* GcRoot<mirror::Class> */ out = *address  /* PC-relative */
+      // /* GcRoot<mirror::String> */ out = *address  /* PC-relative */
       GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
       SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
       codegen_->AddSlowPath(slow_path);
@@ -6289,6 +6298,14 @@
       __ Bind(slow_path->GetExitLabel());
       return;
     }
+    case HLoadString::LoadKind::kJitTableAddress: {
+      Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset);
+      Label* fixup_label = codegen_->NewJitRootStringPatch(
+          load->GetDexFile(), load->GetStringIndex());
+      // /* GcRoot<mirror::String> */ out = *address
+      GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+      return;
+    }
     default:
       break;
   }
@@ -6849,24 +6866,24 @@
                                           temp_loc,
                                           iftable_offset,
                                           kWithoutReadBarrier);
-        // Null iftable means it is empty.
-        __ testl(temp, temp);
-        __ j(kZero, type_check_slow_path->GetEntryLabel());
-
-        // Loop through the iftable and check if any class matches.
+        // Iftable is never null.
         __ movl(maybe_temp2_loc.AsRegister<Register>(), Address(temp, array_length_offset));
-
+        // Loop through the iftable and check if any class matches.
         NearLabel start_loop;
         __ Bind(&start_loop);
-        __ cmpl(cls.AsRegister<Register>(), Address(temp, object_array_data_offset));
-        __ j(kEqual, &done);  // Return if same class.
-        // Go to next interface.
-        __ addl(temp, Immediate(2 * kHeapReferenceSize));
+        // Need to subtract first to handle the empty array case.
         __ subl(maybe_temp2_loc.AsRegister<Register>(), Immediate(2));
-        __ j(kNotZero, &start_loop);
+        __ j(kNegative, type_check_slow_path->GetEntryLabel());
+        // Go to next interface if the classes do not match.
+        __ cmpl(cls.AsRegister<Register>(),
+                CodeGeneratorX86::ArrayAddress(temp,
+                                               maybe_temp2_loc,
+                                               TIMES_4,
+                                               object_array_data_offset));
+        __ j(kNotEqual, &start_loop);
+      } else {
+        __ jmp(type_check_slow_path->GetEntryLabel());
       }
-
-      __ jmp(type_check_slow_path->GetEntryLabel());
       break;
     }
   }
@@ -7562,7 +7579,7 @@
     // The value to patch is the distance from the offset in the constant area
     // from the address computed by the HX86ComputeBaseMethodAddress instruction.
     int32_t constant_offset = codegen_->ConstantAreaStart() + offset_into_constant_area_;
-    int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();;
+    int32_t relative_position = constant_offset - codegen_->GetMethodAddressOffset();
 
     // Patch in the right value.
     region.StoreUnaligned<int32_t>(pos - 4, relative_position);
@@ -7736,6 +7753,20 @@
   }
 }
 
+void CodeGeneratorX86::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const PatchInfo<Label>& info : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index));
+    DCHECK(it != jit_string_roots_.end());
+    size_t index_in_table = it->second;
+    uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+    uintptr_t address =
+        reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+    typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+    reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] =
+       dchecked_integral_cast<uint32_t>(address);
+  }
+}
+
 #undef __
 
 }  // namespace x86
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 164231b..16ea6b5 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -414,12 +414,15 @@
   void RecordTypePatch(HLoadClass* load_class);
   Label* NewStringBssEntryPatch(HLoadString* load_string);
   Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+  Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index);
 
   void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
 
   // Emit linker patches.
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   // Emit a write barrier.
   void MarkGCCard(Register temp,
                   Register card,
@@ -616,6 +619,9 @@
   // Type patch locations.
   ArenaDeque<PatchInfo<Label>> type_patches_;
 
+  // Patches for string root accesses in JIT compiled code.
+  ArenaDeque<PatchInfo<Label>> jit_string_patches_;
+
   // Offset to the start of the constant area in the assembled code.
   // Used for fixups to the constant area.
   int32_t constant_area_start_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 2425a4c..3467313 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -246,7 +246,8 @@
     SaveLiveRegisters(codegen, locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls_->GetTypeIndex()));
+    __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
+            Immediate(cls_->GetTypeIndex().index_));
     x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType,
                                   at_,
                                   dex_pc_,
@@ -1110,7 +1111,7 @@
 }
 
 void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) {
-  type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex());
+  type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex().index_);
   __ Bind(&type_patches_.back().label);
 }
 
@@ -1258,7 +1259,8 @@
         simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
-        fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+        fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+        jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
   AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
 }
 
@@ -4097,7 +4099,7 @@
 void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
   codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
-                           instruction->GetTypeIndex());
+                           instruction->GetTypeIndex().index_);
   // Note: if heap poisoning is enabled, the entry point takes cares
   // of poisoning the reference.
   codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
@@ -5484,7 +5486,7 @@
 void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
   LocationSummary* locations = cls->GetLocations();
   if (cls->NeedsAccessCheck()) {
-    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+    codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex().index_);
     codegen_->InvokeRuntime(kQuickInitializeTypeAndVerifyAccess, cls, cls->GetDexPc());
     CheckEntrypointTypes<kQuickInitializeTypeAndVerifyAccess, void*, uint32_t>();
     return;
@@ -5567,7 +5569,7 @@
       GenerateGcRootFieldLoad(
           cls,
           out_loc,
-          Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+          Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)),
           /* fixup_label */ nullptr,
           read_barrier_option);
       generate_null_check = !cls->IsInDexCache();
@@ -5630,16 +5632,15 @@
       break;
     case HLoadString::LoadKind::kDexCacheViaMethod:
       break;
+    case HLoadString::LoadKind::kJitTableAddress:
+      DCHECK(Runtime::Current()->UseJitCompilation());
+      break;
   }
   return desired_string_load_kind;
 }
 
 void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
-      ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
-          ? LocationSummary::kCallOnMainOnly
-          : LocationSummary::kCallOnSlowPath)
-      : LocationSummary::kNoCall;
+  LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetOut(Location::RegisterLocation(RAX));
@@ -5659,6 +5660,14 @@
   }
 }
 
+Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) {
+  jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u);
+  // Add a patch entry and return the label.
+  jit_string_patches_.emplace_back(dex_file, dex_index);
+  PatchInfo<Label>* info = &jit_string_patches_.back();
+  return &info->label;
+}
+
 void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
@@ -5690,6 +5699,15 @@
       __ Bind(slow_path->GetExitLabel());
       return;
     }
+    case HLoadString::LoadKind::kJitTableAddress: {
+      Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
+                                          /* no_rip */ true);
+      Label* fixup_label =
+          codegen_->NewJitRootStringPatch(load->GetDexFile(), load->GetStringIndex());
+      // /* GcRoot<mirror::String> */ out = *address
+      GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+      return;
+    }
     default:
       break;
   }
@@ -6259,23 +6277,24 @@
                                           temp_loc,
                                           iftable_offset,
                                           kWithoutReadBarrier);
-        // Null iftable means it is empty.
-        __ testl(temp, temp);
-        __ j(kZero, type_check_slow_path->GetEntryLabel());
-
-        // Loop through the iftable and check if any class matches.
+        // Iftable is never null.
         __ movl(maybe_temp2_loc.AsRegister<CpuRegister>(), Address(temp, array_length_offset));
-
+        // Loop through the iftable and check if any class matches.
         NearLabel start_loop;
         __ Bind(&start_loop);
-        __ cmpl(cls.AsRegister<CpuRegister>(), Address(temp, object_array_data_offset));
-        __ j(kEqual, &done);  // Return if same class.
-        // Go to next interface.
-        __ addl(temp, Immediate(2 * kHeapReferenceSize));
+        // Need to subtract first to handle the empty array case.
         __ subl(maybe_temp2_loc.AsRegister<CpuRegister>(), Immediate(2));
-        __ j(kNotZero, &start_loop);
+        __ j(kNegative, type_check_slow_path->GetEntryLabel());
+        // Go to next interface if the classes do not match.
+        __ cmpl(cls.AsRegister<CpuRegister>(),
+                CodeGeneratorX86_64::ArrayAddress(temp,
+                                                  maybe_temp2_loc,
+                                                  TIMES_4,
+                                                  object_array_data_offset));
+        __ j(kNotEqual, &start_loop);  // Return if same class.
+      } else {
+        __ jmp(type_check_slow_path->GetEntryLabel());
       }
-      __ jmp(type_check_slow_path->GetEntryLabel());
       break;
   }
 
@@ -7090,6 +7109,20 @@
   }
 }
 
+void CodeGeneratorX86_64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
+  for (const PatchInfo<Label>& info : jit_string_patches_) {
+    const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index));
+    DCHECK(it != jit_string_roots_.end());
+    size_t index_in_table = it->second;
+    uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+    uintptr_t address =
+        reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
+    typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+    reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] =
+       dchecked_integral_cast<uint32_t>(address);
+  }
+}
+
 #undef __
 
 }  // namespace x86_64
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index e5a4152..0f70b15 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -412,11 +412,14 @@
   void RecordTypePatch(HLoadClass* load_class);
   Label* NewStringBssEntryPatch(HLoadString* load_string);
   Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
+  Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index);
 
   void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
 
   void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
 
+  void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE;
+
   const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const {
     return isa_features_;
   }
@@ -602,6 +605,9 @@
   // Fixups for jump tables need to be handled specially.
   ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
 
+  // Patches for string literals in JIT compiled code.
+  ArenaDeque<PatchInfo<Label>> jit_string_patches_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64);
 };
 
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 5129daf..d3623f1 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -139,9 +139,14 @@
   HConstant* instr = location.GetConstant();
   if (instr->IsIntConstant()) {
     return instr->AsIntConstant()->GetValue();
-  } else {
-    DCHECK(instr->IsNullConstant()) << instr->DebugName();
+  } else if (instr->IsNullConstant()) {
     return 0;
+  } else {
+    DCHECK(instr->IsLongConstant()) << instr->DebugName();
+    const int64_t ret = instr->AsLongConstant()->GetValue();
+    DCHECK_GE(ret, std::numeric_limits<int32_t>::min());
+    DCHECK_LE(ret, std::numeric_limits<int32_t>::max());
+    return ret;
   }
 }
 
@@ -161,7 +166,7 @@
   if (location.IsRegister()) {
     return vixl::aarch32::Operand(RegisterFrom(location, type));
   } else {
-    return vixl::aarch32::Operand(Int64ConstantFrom(location));
+    return vixl::aarch32::Operand(Int32ConstantFrom(location));
   }
 }
 
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 5fac3ac..7ef28ed 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -756,7 +756,7 @@
 
   // Make various unsigned comparisons with zero against a parameter.
   HInstruction* parameter = new (&allocator_) HParameterValue(
-      graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true);
+      graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt, true);
   entry_block->AddInstruction(parameter);
   entry_block->AddInstruction(new (&allocator_) HGoto());
 
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
new file mode 100644
index 0000000..c80e19e
--- /dev/null
+++ b/compiler/optimizing/escape.cc
@@ -0,0 +1,100 @@
+/*
+ * 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 "escape.h"
+
+#include "nodes.h"
+
+namespace art {
+
+void CalculateEscape(HInstruction* reference,
+                     bool (*no_escape)(HInstruction*, HInstruction*),
+                     /*out*/ bool* is_singleton,
+                     /*out*/ bool* is_singleton_and_non_escaping) {
+  // For references not allocated in the method, don't assume anything.
+  if (!reference->IsNewInstance() && !reference->IsNewArray()) {
+    *is_singleton = false;
+    *is_singleton_and_non_escaping = false;
+    return;
+  }
+  // Assume the best until proven otherwise.
+  *is_singleton = true;
+  *is_singleton_and_non_escaping = true;
+  // Visit all uses to determine if this reference can escape into the heap,
+  // a method call, an alias, etc.
+  for (const HUseListNode<HInstruction*>& use : reference->GetUses()) {
+    HInstruction* user = use.GetUser();
+    if (no_escape != nullptr && (*no_escape)(reference, user)) {
+      // Client supplied analysis says there is no escape.
+      continue;
+    } else if (user->IsBoundType() || user->IsNullCheck()) {
+      // BoundType shouldn't normally be necessary for an allocation. Just be conservative
+      // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit
+      // allocations, but if we see one before it is simplified, assume an alias.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
+               (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsStaticFieldSet() && (reference == user->InputAt(1))) ||
+               (user->IsUnresolvedStaticFieldSet() && (reference == user->InputAt(0))) ||
+               (user->IsArraySet() && (reference == user->InputAt(2)))) {
+      // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap.
+      // Hence, the reference is no longer the only name that can refer to its value.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) ||
+               (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) {
+      // The field is accessed in an unresolved way. We mark the object as a non-singleton.
+      // Note that we could optimize this case and still perform some optimizations until
+      // we hit the unresolved access, but the conservative assumption is the simplest.
+      *is_singleton = false;
+      *is_singleton_and_non_escaping = false;
+      return;
+    } else if (user->IsReturn()) {
+      *is_singleton_and_non_escaping = false;
+    }
+  }
+
+  // Need for further analysis?
+  if (!*is_singleton_and_non_escaping) {
+    return;
+  }
+
+  // Look at the environment uses and if it's for HDeoptimize, it's treated the
+  // same as a return which escapes at the end of executing the compiled code.
+  // Other environment uses are fine, as long as all client optimizations that
+  // rely on this informations are disabled for debuggable.
+  for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) {
+    HEnvironment* user = use.GetUser();
+    if (user->GetHolder()->IsDeoptimize()) {
+      *is_singleton_and_non_escaping = false;
+      break;
+    }
+  }
+}
+
+bool IsNonEscapingSingleton(HInstruction* reference,
+                            bool (*no_escape)(HInstruction*, HInstruction*)) {
+  bool is_singleton = true;
+  bool is_singleton_and_non_escaping = true;
+  CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping);
+  return is_singleton_and_non_escaping;
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h
new file mode 100644
index 0000000..6514843
--- /dev/null
+++ b/compiler/optimizing/escape.h
@@ -0,0 +1,59 @@
+/*
+ * 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_ESCAPE_H_
+#define ART_COMPILER_OPTIMIZING_ESCAPE_H_
+
+namespace art {
+
+class HInstruction;
+
+/*
+ * Methods related to escape analysis, i.e. determining whether an object
+ * allocation is visible outside ('escapes') its immediate method context.
+ */
+
+/*
+ * Performs escape analysis on the given instruction, typically a reference to an
+ * allocation. The method assigns true to parameter 'is_singleton' if the reference
+ * is the only name that can refer to its value during the lifetime of the method,
+ * meaning that the reference is not aliased with something else, is not stored to
+ * heap memory, and not passed to another method. The method assigns true to parameter
+ * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned
+ * to the caller or used as an environment local of an HDeoptimize instruction.
+ *
+ * When set, the no_escape function is applied to any use of the allocation instruction
+ * prior to any built-in escape analysis. This allows clients to define better escape
+ * analysis in certain case-specific circumstances. If 'no_escape(reference, user)'
+ * returns true, the user is assumed *not* to cause any escape right away. The return
+ * value false means the client cannot provide a definite answer and built-in escape
+ * analysis is applied to the user instead.
+ */
+void CalculateEscape(HInstruction* reference,
+                     bool (*no_escape)(HInstruction*, HInstruction*),
+                     /*out*/ bool* is_singleton,
+                     /*out*/ bool* is_singleton_and_non_escaping);
+
+/*
+ * Convenience method for testing singleton and non-escaping property at once.
+ * Callers should be aware that this method invokes the full analysis at each call.
+ */
+bool IsNonEscapingSingleton(HInstruction* reference,
+                            bool (*no_escape)(HInstruction*, HInstruction*));
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_ESCAPE_H_
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 6abf00e..437d35c 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -35,7 +35,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(),
-                                                             0,
+                                                             dex::TypeIndex(0),
                                                              0,
                                                              Primitive::kPrimNot);
   entry->AddInstruction(parameter);
@@ -120,7 +120,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(),
-                                                             0,
+                                                             dex::TypeIndex(0),
                                                              0,
                                                              Primitive::kPrimNot);
   entry->AddInstruction(parameter);
@@ -204,7 +204,7 @@
   graph->SetEntryBlock(entry);
 
   HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(),
-                                                             0,
+                                                             dex::TypeIndex(0),
                                                              0,
                                                              Primitive::kPrimNot);
   entry->AddInstruction(parameter);
@@ -352,7 +352,7 @@
   inner_loop_exit->AddSuccessor(outer_loop_header);
 
   HInstruction* parameter = new (&allocator) HParameterValue(graph->GetDexFile(),
-                                                             0,
+                                                             dex::TypeIndex(0),
                                                              0,
                                                              Primitive::kPrimBoolean);
   entry->AddInstruction(parameter);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 031f1d7..3425b88 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -80,7 +80,7 @@
 
     // Provide entry and exit instructions.
     parameter_ = new (&allocator_) HParameterValue(
-        graph_->GetDexFile(), 0, 0, Primitive::kPrimNot, true);
+        graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot, true);
     entry_->AddInstruction(parameter_);
     constant0_ = graph_->GetIntConstant(0);
     constant1_ = graph_->GetIntConstant(1);
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 8bbdd4a..4c99e3c 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -62,9 +62,15 @@
     graph_->SetEntryBlock(entry_block_);
     graph_->SetExitBlock(exit_block_);
     // Two parameters.
-    x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
+    x_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+                                           dex::TypeIndex(0),
+                                           0,
+                                           Primitive::kPrimInt);
     entry_block_->AddInstruction(x_);
-    y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
+    y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+                                           dex::TypeIndex(0),
+                                           0,
+                                           Primitive::kPrimInt);
     entry_block_->AddInstruction(y_);
     // Set arbitrary range analysis hint while testing private methods.
     SetHint(x_);
@@ -572,7 +578,8 @@
   HInstruction* new_array = new (&allocator_)
       HNewArray(x_,
                 graph_->GetCurrentMethod(),
-                0, Primitive::kPrimInt,
+                0,
+                dex::TypeIndex(Primitive::kPrimInt),
                 graph_->GetDexFile(),
                 kQuickAllocArray);
   entry_block_->AddInstruction(new_array);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 7fe54b9..16a465a 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -197,15 +197,15 @@
   }
 }
 
-static uint32_t FindClassIndexIn(mirror::Class* cls,
-                                 const DexFile& dex_file,
-                                 Handle<mirror::DexCache> dex_cache)
+static dex::TypeIndex FindClassIndexIn(mirror::Class* cls,
+                                       const DexFile& dex_file,
+                                       Handle<mirror::DexCache> dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  uint32_t index = DexFile::kDexNoIndex;
+  dex::TypeIndex index;
   if (cls->GetDexCache() == nullptr) {
     DCHECK(cls->IsArrayClass()) << cls->PrettyClass();
     index = cls->FindTypeIndexInOtherDexFile(dex_file);
-  } else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) {
+  } else if (!cls->GetDexTypeIndex().IsValid()) {
     DCHECK(cls->IsProxyClass()) << cls->PrettyClass();
     // TODO: deal with proxy classes.
   } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
@@ -223,8 +223,8 @@
     // We cannot guarantee the entry in the dex cache will resolve to the same class,
     // as there may be different class loaders. So only return the index if it's
     // the right class in the dex cache already.
-    if (index != DexFile::kDexNoIndex && dex_cache->GetResolvedType(index) != cls) {
-      index = DexFile::kDexNoIndex;
+    if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) {
+      index = dex::TypeIndex::Invalid();
     }
   }
 
@@ -363,9 +363,9 @@
       << invoke_instruction->DebugName();
 
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
-  uint32_t class_index = FindClassIndexIn(
+  dex::TypeIndex class_index = FindClassIndexIn(
       ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache());
-  if (class_index == DexFile::kDexNoIndex) {
+  if (!class_index.IsValid()) {
     VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
                    << " from inline cache is not inlined because its class is not"
                    << " accessible to the caller";
@@ -417,7 +417,7 @@
 HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
                                      HInstruction* cursor,
                                      HBasicBlock* bb_cursor,
-                                     uint32_t class_index,
+                                     dex::TypeIndex class_index,
                                      bool is_referrer,
                                      HInstruction* invoke_instruction,
                                      bool with_deoptimization) {
@@ -489,10 +489,10 @@
     HInstruction* cursor = invoke_instruction->GetPrevious();
     HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
 
-    uint32_t class_index = FindClassIndexIn(
+    dex::TypeIndex class_index = FindClassIndexIn(
         ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
     HInstruction* return_replacement = nullptr;
-    if (class_index == DexFile::kDexNoIndex ||
+    if (!class_index.IsValid() ||
         !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
       all_targets_inlined = false;
     } else {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index a1dcd58..682393e 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
 #define ART_COMPILER_OPTIMIZING_INLINER_H_
 
+#include "dex_file_types.h"
 #include "invoke_type.h"
 #include "optimization.h"
 
@@ -150,7 +151,7 @@
   HInstruction* AddTypeGuard(HInstruction* receiver,
                              HInstruction* cursor,
                              HBasicBlock* bb_cursor,
-                             uint32_t class_index,
+                             dex::TypeIndex class_index,
                              bool is_referrer,
                              HInstruction* invoke_instruction,
                              bool with_deoptimization)
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c8c4ca7..40de5ce 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -908,7 +908,7 @@
                       false /* is_unresolved */);
 }
 
-bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
+bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
@@ -1004,7 +1004,7 @@
   Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
 
   // The index at which the method's class is stored in the DexCache's type array.
-  uint32_t storage_index = DexFile::kDexNoIndex;
+  dex::TypeIndex storage_index;
   bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
   if (is_outer_class) {
     storage_index = outer_class->GetDexTypeIndex();
@@ -1021,7 +1021,7 @@
 
   if (IsInitialized(resolved_method_class)) {
     *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
-  } else if (storage_index != DexFile::kDexNoIndex) {
+  } else if (storage_index.IsValid()) {
     *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
     HLoadClass* load_class = new (arena_) HLoadClass(
         graph_->GetCurrentMethod(),
@@ -1297,7 +1297,7 @@
   return GetClassFrom(compiler_driver_, *dex_compilation_unit_);
 }
 
-bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
+bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
@@ -1360,7 +1360,7 @@
   Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
 
   // The index at which the field's class is stored in the DexCache's type array.
-  uint32_t storage_index;
+  dex::TypeIndex storage_index;
   bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass());
   if (is_outer_class) {
     storage_index = outer_class->GetDexTypeIndex();
@@ -1497,7 +1497,7 @@
 }
 
 void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc,
-                                              uint32_t type_index,
+                                              dex::TypeIndex type_index,
                                               uint32_t number_of_vreg_arguments,
                                               bool is_range,
                                               uint32_t* args,
@@ -1644,7 +1644,7 @@
 void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
                                          uint8_t destination,
                                          uint8_t reference,
-                                         uint16_t type_index,
+                                         dex::TypeIndex type_index,
                                          uint32_t dex_pc) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
@@ -1684,14 +1684,14 @@
   }
 }
 
-bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index,
+bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index,
                                            Handle<mirror::DexCache> dex_cache,
                                            bool* finalizable) const {
   return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
       dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable);
 }
 
-bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
+bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const {
   ScopedObjectAccess soa(Thread::Current());
   Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
   return NeedsAccessCheck(type_index, dex_cache, finalizable);
@@ -1723,7 +1723,10 @@
     if (dex_pc_in_map == dex_pc) {
       return value_in_map;
     } else {
-      skipped_interpreter_metadata_.Put(dex_pc_in_map, value_in_map);
+      // Overwrite and not Put, as quickened CHECK-CAST has two entries with
+      // the same dex_pc. This is OK, because the compiler does not care about those
+      // entries.
+      skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map);
     }
   }
 }
@@ -2446,7 +2449,7 @@
     }
 
     case Instruction::NEW_INSTANCE: {
-      if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) {
+      if (!BuildNewInstance(dex::TypeIndex(instruction.VRegB_21c()), dex_pc)) {
         return false;
       }
       UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
@@ -2454,7 +2457,7 @@
     }
 
     case Instruction::NEW_ARRAY: {
-      uint16_t type_index = instruction.VRegC_22c();
+      dex::TypeIndex type_index(instruction.VRegC_22c());
       HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
       bool finalizable;
       QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
@@ -2472,7 +2475,7 @@
 
     case Instruction::FILLED_NEW_ARRAY: {
       uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
-      uint32_t type_index = instruction.VRegB_35c();
+      dex::TypeIndex type_index(instruction.VRegB_35c());
       uint32_t args[5];
       instruction.GetVarArgs(args);
       BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0);
@@ -2481,7 +2484,7 @@
 
     case Instruction::FILLED_NEW_ARRAY_RANGE: {
       uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
-      uint32_t type_index = instruction.VRegB_3rc();
+      dex::TypeIndex type_index(instruction.VRegB_3rc());
       uint32_t register_index = instruction.VRegC_3rc();
       BuildFilledNewArray(
           dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index);
@@ -2638,7 +2641,7 @@
     }
 
     case Instruction::CONST_CLASS: {
-      uint16_t type_index = instruction.VRegB_21c();
+      dex::TypeIndex type_index(instruction.VRegB_21c());
       // `CanAccessTypeWithoutChecks` will tell whether the method being
       // built is trying to access its own class, so that the generated
       // code can optimize for this case. However, the optimization does not
@@ -2679,14 +2682,14 @@
     case Instruction::INSTANCE_OF: {
       uint8_t destination = instruction.VRegA_22c();
       uint8_t reference = instruction.VRegB_22c();
-      uint16_t type_index = instruction.VRegC_22c();
+      dex::TypeIndex type_index(instruction.VRegC_22c());
       BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
       break;
     }
 
     case Instruction::CHECK_CAST: {
       uint8_t reference = instruction.VRegA_21c();
-      uint16_t type_index = instruction.VRegB_21c();
+      dex::TypeIndex type_index(instruction.VRegB_21c());
       BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
       break;
     }
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index aa34ddd..f29e522 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -20,6 +20,7 @@
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
 #include "block_builder.h"
+#include "dex_file_types.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_driver-inl.h"
 #include "driver/dex_compilation_unit.h"
@@ -100,11 +101,11 @@
 
   // Returns whether the current method needs access check for the type.
   // Output parameter finalizable is set to whether the type is finalizable.
-  bool NeedsAccessCheck(uint32_t type_index,
+  bool NeedsAccessCheck(dex::TypeIndex type_index,
                         Handle<mirror::DexCache> dex_cache,
                         /*out*/bool* finalizable) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
+  bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const;
 
   template<typename T>
   void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
@@ -176,7 +177,7 @@
 
   // Builds a new array node and the instructions that fill it.
   void BuildFilledNewArray(uint32_t dex_pc,
-                           uint32_t type_index,
+                           dex::TypeIndex type_index,
                            uint32_t number_of_vreg_arguments,
                            bool is_range,
                            uint32_t* args,
@@ -205,7 +206,7 @@
   void BuildTypeCheck(const Instruction& instruction,
                       uint8_t destination,
                       uint8_t reference,
-                      uint16_t type_index,
+                      dex::TypeIndex type_index,
                       uint32_t dex_pc);
 
   // Builds an instruction sequence for a switch statement.
@@ -218,7 +219,7 @@
   mirror::Class* GetCompilingClass() const;
 
   // Returns whether `type_index` points to the outer-most compiling method's class.
-  bool IsOutermostCompilingClass(uint16_t type_index) const;
+  bool IsOutermostCompilingClass(dex::TypeIndex type_index) const;
 
   void PotentiallySimplifyFakeString(uint16_t original_dex_register,
                                      uint32_t dex_pc,
@@ -258,7 +259,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Build a HNewInstance instruction.
-  bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
+  bool BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc);
 
   // Return whether the compiler can assume `cls` is initialized.
   bool IsInitialized(Handle<mirror::Class> cls) const
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 451abc5..17a97da 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2165,11 +2165,11 @@
   __ Cbz(dst, slow_path->GetEntryLabel());
 
   if (!length.IsConstant()) {
-    // If the length is negative, bail out.
-    __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel());
-    // If the length > 32 then (currently) prefer libcore's native implementation.
+    // Merge the following two comparisons into one:
+    //   If the length is negative, bail out (delegate to libcore's native implementation).
+    //   If the length > 32 then (currently) prefer libcore's native implementation.
     __ Cmp(WRegisterFrom(length), kSystemArrayCopyCharThreshold);
-    __ B(slow_path->GetEntryLabel(), gt);
+    __ B(slow_path->GetEntryLabel(), hi);
   } else {
     // We have already checked in the LocationsBuilder for the constant case.
     DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0);
@@ -2379,11 +2379,11 @@
   if (!length.IsConstant() &&
       !optimizations.GetCountIsSourceLength() &&
       !optimizations.GetCountIsDestinationLength()) {
-    // If the length is negative, bail out.
-    __ Tbnz(WRegisterFrom(length), kWRegSize - 1, intrinsic_slow_path->GetEntryLabel());
-    // If the length >= 128 then (currently) prefer native implementation.
+    // Merge the following two comparisons into one:
+    //   If the length is negative, bail out (delegate to libcore's native implementation).
+    //   If the length >= 128 then (currently) prefer native implementation.
     __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold);
-    __ B(intrinsic_slow_path->GetEntryLabel(), ge);
+    __ B(intrinsic_slow_path->GetEntryLabel(), hs);
   }
   // Validity checks: source.
   CheckSystemArrayCopyPosition(masm,
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index e4bef34..c8e3534 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -303,7 +303,7 @@
     vixl32::Register in_reg_hi = HighRegisterFrom(in);
     vixl32::Label end;
     __ Clz(out, in_reg_hi);
-    __ Cbnz(in_reg_hi, &end);
+    __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false);
     __ Clz(out, in_reg_lo);
     __ Add(out, out, 32);
     __ Bind(&end);
@@ -345,7 +345,7 @@
     vixl32::Label end;
     __ Rbit(out, in_reg_lo);
     __ Clz(out, out);
-    __ Cbnz(in_reg_lo, &end);
+    __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false);
     __ Rbit(out, in_reg_hi);
     __ Clz(out, out);
     __ Add(out, out, 32);
@@ -518,7 +518,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekByte(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldrsb(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldrsb(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
@@ -528,7 +528,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekIntNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldr(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldr(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPeekLongNative(HInvoke* invoke) {
@@ -545,9 +545,9 @@
   vixl32::Register hi = HighRegisterFrom(invoke->GetLocations()->Out());
   if (addr.Is(lo)) {
     __ Ldr(hi, MemOperand(addr, 4));
-    __ Ldr(lo, addr);
+    __ Ldr(lo, MemOperand(addr));
   } else {
-    __ Ldr(lo, addr);
+    __ Ldr(lo, MemOperand(addr));
     __ Ldr(hi, MemOperand(addr, 4));
   }
 }
@@ -559,7 +559,7 @@
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPeekShortNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
   // Ignore upper 4B of long address.
-  __ Ldrsh(OutputRegister(invoke), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Ldrsh(OutputRegister(invoke), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -576,7 +576,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeByte(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Strb(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Strb(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
@@ -585,7 +585,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeIntNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Str(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Str(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitMemoryPokeLongNative(HInvoke* invoke) {
@@ -598,7 +598,7 @@
   vixl32::Register addr = LowRegisterFrom(invoke->GetLocations()->InAt(0));
   // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
   // exception. So we can't use ldrd as addr may be unaligned.
-  __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), addr);
+  __ Str(LowRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr));
   __ Str(HighRegisterFrom(invoke->GetLocations()->InAt(1)), MemOperand(addr, 4));
 }
 
@@ -608,7 +608,7 @@
 
 void IntrinsicCodeGeneratorARMVIXL::VisitMemoryPokeShortNative(HInvoke* invoke) {
   ArmVIXLAssembler* assembler = GetAssembler();
-  __ Strh(InputRegisterAt(invoke, 1), LowRegisterFrom(invoke->GetLocations()->InAt(0)));
+  __ Strh(InputRegisterAt(invoke, 1), MemOperand(LowRegisterFrom(invoke->GetLocations()->InAt(0))));
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitThreadCurrentThread(HInvoke* invoke) {
@@ -842,8 +842,8 @@
       __ Add(temp_reg, base, offset);
       vixl32::Label loop_head;
       __ Bind(&loop_head);
-      __ Ldrexd(temp_lo, temp_hi, temp_reg);
-      __ Strexd(temp_lo, value_lo, value_hi, temp_reg);
+      __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg));
+      __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg));
       __ Cmp(temp_lo, 0);
       __ B(ne, &loop_head);
     } else {
@@ -1042,7 +1042,7 @@
   vixl32::Label loop_head;
   __ Bind(&loop_head);
 
-  __ Ldrex(tmp, tmp_ptr);
+  __ Ldrex(tmp, MemOperand(tmp_ptr));
 
   __ Subs(tmp, tmp, expected);
 
@@ -1052,7 +1052,7 @@
                                CodeBufferCheckScope::kMaximumSize);
 
     __ itt(eq);
-    __ strex(eq, tmp, value, tmp_ptr);
+    __ strex(eq, tmp, value, MemOperand(tmp_ptr));
     __ cmp(eq, tmp, 1);
   }
 
@@ -1158,7 +1158,7 @@
   if (can_slow_path) {
     slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
     codegen_->AddSlowPath(slow_path);
-    __ Cbz(arg, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(arg, slow_path->GetEntryLabel());
   }
 
   // Reference equality check, return 0 if same reference.
@@ -1191,7 +1191,9 @@
   }
 
   // Shorter string is empty?
-  __ Cbz(temp0, &end);
+  // Note that mirror::kUseStringCompression==true introduces lots of instructions,
+  // which makes &end label far away from this branch and makes it not 'CBZ-encodable'.
+  __ CompareAndBranchIfZero(temp0, &end, mirror::kUseStringCompression);
 
   if (mirror::kUseStringCompression) {
     // Check if both strings using same compression style to use this comparison loop.
@@ -1218,7 +1220,7 @@
   static_assert(IsAligned<8>(kObjectAlignment),
                 "String data must be 8-byte aligned for unrolled CompareTo loop.");
 
-  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+  const unsigned char_size = Primitive::ComponentSize(Primitive::kPrimChar);
   DCHECK_EQ(char_size, 2u);
 
   UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
@@ -1281,20 +1283,20 @@
     // For compressed strings we need to clear 0x7 from temp1, for uncompressed we need to clear
     // 0xf. We also need to prepare the character extraction mask `uncompressed ? 0xffffu : 0xffu`.
     // The compression flag is now in the highest bit of temp3, so let's play some tricks.
-    __ orr(temp3, temp3, 0xffu << 23);                  // uncompressed ? 0xff800000u : 0x7ff80000u
-    __ bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3));  // &= ~(uncompressed ? 0xfu : 0x7u)
+    __ Orr(temp3, temp3, 0xffu << 23);                  // uncompressed ? 0xff800000u : 0x7ff80000u
+    __ Bic(temp1, temp1, Operand(temp3, vixl32::LSR, 31 - 3));  // &= ~(uncompressed ? 0xfu : 0x7u)
     __ Asr(temp3, temp3, 7u);                           // uncompressed ? 0xffff0000u : 0xff0000u.
     __ Lsr(temp2, temp2, temp1);                        // Extract second character.
     __ Lsr(temp3, temp3, 16u);                          // uncompressed ? 0xffffu : 0xffu
     __ Lsr(out, temp_reg, temp1);                       // Extract first character.
-    __ and_(temp2, temp2, temp3);
-    __ and_(out, out, temp3);
+    __ And(temp2, temp2, temp3);
+    __ And(out, out, temp3);
   } else {
-    __ bic(temp1, temp1, 0xf);
+    __ Bic(temp1, temp1, 0xf);
     __ Lsr(temp2, temp2, temp1);
     __ Lsr(out, temp_reg, temp1);
-    __ movt(temp2, 0);
-    __ movt(out, 0);
+    __ Movt(temp2, 0);
+    __ Movt(out, 0);
   }
 
   __ Sub(out, out, temp2);
@@ -1313,10 +1315,10 @@
     // need to treat as unsigned. Start by freeing the bit with an ADD and continue
     // further down by a LSRS+SBC which will flip the meaning of the flag but allow
     // `subs temp0, #2; bhi different_compression_loop` to serve as the loop condition.
-    __ add(temp0, temp0, temp0);              // Unlike LSL, this ADD is always 16-bit.
+    __ Add(temp0, temp0, temp0);              // Unlike LSL, this ADD is always 16-bit.
     // `temp1` will hold the compressed data pointer, `temp2` the uncompressed data pointer.
-    __ mov(temp1, str);
-    __ mov(temp2, arg);
+    __ Mov(temp1, str);
+    __ Mov(temp2, arg);
     __ Lsrs(temp3, temp3, 1u);                // Continue the move of the compression flag.
     {
       AssemblerAccurateScope aas(assembler->GetVIXLAssembler(),
@@ -1326,11 +1328,11 @@
       __ mov(cs, temp1, arg);                 // Preserves flags.
       __ mov(cs, temp2, str);                 // Preserves flags.
     }
-    __ sbc(temp0, temp0, 0);                  // Complete the move of the compression flag.
+    __ Sbc(temp0, temp0, 0);                  // Complete the move of the compression flag.
 
     // Adjust temp1 and temp2 from string pointers to data pointers.
-    __ add(temp1, temp1, value_offset);
-    __ add(temp2, temp2, value_offset);
+    __ Add(temp1, temp1, value_offset);
+    __ Add(temp2, temp2, value_offset);
 
     vixl32::Label different_compression_loop;
     vixl32::Label different_compression_diff;
@@ -1340,7 +1342,7 @@
     __ Bind(&different_compression_loop);
     __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex));
     __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex));
-    __ cmp(temp_reg, temp3);
+    __ Cmp(temp_reg, temp3);
     __ B(ne, &different_compression_diff);
     __ Subs(temp0, temp0, 2);
     __ B(hi, &different_compression_loop);
@@ -1414,7 +1416,7 @@
   StringEqualsOptimizations optimizations(invoke);
   if (!optimizations.GetArgumentNotNull()) {
     // Check if input is null, return false if it is.
-    __ Cbz(arg, &return_false);
+    __ CompareAndBranchIfZero(arg, &return_false, /* far_target */ false);
   }
 
   // Reference equality check, return true if same reference.
@@ -1442,7 +1444,7 @@
   // Return true if both strings are empty. Even with string compression `count == 0` means empty.
   static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
                 "Expecting 0=compressed, 1=uncompressed");
-  __ Cbz(temp, &return_true);
+  __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
 
   // Assertions that must hold in order to compare strings 4 bytes at a time.
   DCHECK_ALIGNED(value_offset, 4);
@@ -1467,7 +1469,7 @@
   __ Bind(&loop);
   __ Ldr(out, MemOperand(str, temp1));
   __ Ldr(temp2, MemOperand(arg, temp1));
-  __ Add(temp1, temp1, sizeof(uint32_t));
+  __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
   __ Cmp(out, temp2);
   __ B(ne, &return_false);
   // With string compression, we have compared 4 bytes, otherwise 2 chars.
@@ -1718,7 +1720,7 @@
   } else if (length_is_input_length) {
     // The only way the copy can succeed is if pos is zero.
     vixl32::Register pos_reg = RegisterFrom(pos);
-    __ Cbnz(pos_reg, slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(pos_reg, slow_path->GetEntryLabel());
   } else {
     // Check that pos >= 0.
     vixl32::Register pos_reg = RegisterFrom(pos);
@@ -1815,12 +1817,12 @@
 
   if (!optimizations.GetSourceIsNotNull()) {
     // Bail out if the source is null.
-    __ Cbz(src, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(src, intrinsic_slow_path->GetEntryLabel());
   }
 
   if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) {
     // Bail out if the destination is null.
-    __ Cbz(dest, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfZero(dest, intrinsic_slow_path->GetEntryLabel());
   }
 
   // If the length is negative, bail out.
@@ -1865,13 +1867,13 @@
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
             invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-        __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp1, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp1` has been unpoisoned
         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
         // /* uint16_t */ temp1 = static_cast<uint16>(temp1->primitive_type_);
         __ Ldrh(temp1, MemOperand(temp1, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
       }
 
       // /* HeapReference<Class> */ temp1 = dest->klass_
@@ -1889,13 +1891,13 @@
         // /* HeapReference<Class> */ temp2 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
             invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
-        __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp2, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp2` has been unpoisoned
         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
         // /* uint16_t */ temp2 = static_cast<uint16>(temp2->primitive_type_);
         __ Ldrh(temp2, MemOperand(temp2, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp2, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp2, intrinsic_slow_path->GetEntryLabel());
       }
 
       // For the same reason given earlier, `temp1` is not trashed by the
@@ -1918,7 +1920,7 @@
         // comparison with null below, and this reference is not
         // kept afterwards.
         __ Ldr(temp1, MemOperand(temp1, super_offset));
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
         __ Bind(&do_copy);
       } else {
         __ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1944,24 +1946,24 @@
         // Bail out if the destination is not a non primitive array.
         // /* HeapReference<Class> */ temp3 = temp1->component_type_
         __ Ldr(temp3, MemOperand(temp1, component_offset));
-        __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
         assembler->MaybeUnpoisonHeapReference(temp3);
         // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
         __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
       }
 
       if (!optimizations.GetSourceIsNonPrimitiveArray()) {
         // Bail out if the source is not a non primitive array.
         // /* HeapReference<Class> */ temp3 = temp2->component_type_
         __ Ldr(temp3, MemOperand(temp2, component_offset));
-        __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
         assembler->MaybeUnpoisonHeapReference(temp3);
         // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
         __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
         static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-        __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
       }
 
       __ Cmp(temp1, temp2);
@@ -1978,7 +1980,7 @@
         // /* HeapReference<Class> */ temp1 = temp1->super_class_
         __ Ldr(temp1, MemOperand(temp1, super_offset));
         // No need to unpoison the result, we're comparing against null.
-        __ Cbnz(temp1, intrinsic_slow_path->GetEntryLabel());
+        __ CompareAndBranchIfNonZero(temp1, intrinsic_slow_path->GetEntryLabel());
         __ Bind(&do_copy);
       } else {
         __ B(ne, intrinsic_slow_path->GetEntryLabel());
@@ -1994,7 +1996,7 @@
       // /* HeapReference<Class> */ temp3 = temp1->component_type_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
           invoke, temp3_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
-      __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
       // If heap poisoning is enabled, `temp3` has been unpoisoned
       // by the the previous call to GenerateFieldLoadWithBakerReadBarrier.
     } else {
@@ -2003,13 +2005,13 @@
       assembler->MaybeUnpoisonHeapReference(temp1);
       // /* HeapReference<Class> */ temp3 = temp1->component_type_
       __ Ldr(temp3, MemOperand(temp1, component_offset));
-      __ Cbz(temp3, intrinsic_slow_path->GetEntryLabel());
+      __ CompareAndBranchIfZero(temp3, intrinsic_slow_path->GetEntryLabel());
       assembler->MaybeUnpoisonHeapReference(temp3);
     }
     // /* uint16_t */ temp3 = static_cast<uint16>(temp3->primitive_type_);
     __ Ldrh(temp3, MemOperand(temp3, primitive_offset));
     static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
-    __ Cbnz(temp3, intrinsic_slow_path->GetEntryLabel());
+    __ CompareAndBranchIfNonZero(temp3, intrinsic_slow_path->GetEntryLabel());
   }
 
   int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot);
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 2a62643..8c34dc6 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -63,7 +63,10 @@
     return_->AddSuccessor(exit_);
 
     // Provide boiler-plate instructions.
-    parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);
+    parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+                                                   dex::TypeIndex(0),
+                                                   0,
+                                                   Primitive::kPrimNot);
     entry_->AddInstruction(parameter_);
     int_constant_ = graph_->GetIntConstant(42);
     float_constant_ = graph_->GetFloatConstant(42.0f);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 15e6059..edecf17 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -15,6 +15,8 @@
  */
 
 #include "load_store_elimination.h"
+
+#include "escape.h"
 #include "side_effects_analysis.h"
 
 #include <iostream>
@@ -31,70 +33,12 @@
 // whether it's a singleton, returned, etc.
 class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
  public:
-  ReferenceInfo(HInstruction* reference, size_t pos) : reference_(reference), position_(pos) {
-    is_singleton_ = true;
-    is_singleton_and_non_escaping_ = true;
-    if (!reference_->IsNewInstance() && !reference_->IsNewArray()) {
-      // For references not allocated in the method, don't assume anything.
-      is_singleton_ = false;
-      is_singleton_and_non_escaping_ = false;
-      return;
-    }
-
-    // Visit all uses to determine if this reference can spread into the heap,
-    // a method call, etc.
-    for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
-      HInstruction* user = use.GetUser();
-      DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated";
-      if (user->IsBoundType()) {
-        // BoundType shouldn't normally be necessary for a NewInstance.
-        // Just be conservative for the uncommon cases.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
-          (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) ||
-          (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) ||
-          (user->IsArraySet() && (reference_ == user->InputAt(2)))) {
-        // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
-        // reference_ isn't the only name that can refer to its value anymore.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) ||
-          (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) {
-        // The field is accessed in an unresolved way. We mark the object as a non-singleton
-        // to disable load/store optimizations on it.
-        // Note that we could optimize this case and still perform some optimizations until
-        // we hit the unresolved access, but disabling is the simplest.
-        is_singleton_ = false;
-        is_singleton_and_non_escaping_ = false;
-        return;
-      }
-      if (user->IsReturn()) {
-        is_singleton_and_non_escaping_ = false;
-      }
-    }
-
-    if (!is_singleton_ || !is_singleton_and_non_escaping_) {
-      return;
-    }
-
-    // Look at Environment uses and if it's for HDeoptimize, it's treated the same
-    // as a return which escapes at the end of executing the compiled code. We don't
-    // do store elimination for singletons that escape through HDeoptimize.
-    // Other Environment uses are fine since LSE is disabled for debuggable.
-    for (const HUseListNode<HEnvironment*>& use : reference_->GetEnvUses()) {
-      HEnvironment* user = use.GetUser();
-      if (user->GetHolder()->IsDeoptimize()) {
-        is_singleton_and_non_escaping_ = false;
-        break;
-      }
-    }
+  ReferenceInfo(HInstruction* reference, size_t pos)
+      : reference_(reference),
+        position_(pos),
+        is_singleton_(true),
+        is_singleton_and_non_escaping_(true) {
+    CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_);
   }
 
   HInstruction* GetReference() const {
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index 7805a69..9a6b493 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -48,7 +48,10 @@
     graph_->AddBlock(exit_block_);
     graph_->SetEntryBlock(entry_block_);
     graph_->SetExitBlock(exit_block_);
-    parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
+    parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+                                                   dex::TypeIndex(0),
+                                                   0,
+                                                   Primitive::kPrimInt);
     entry_block_->AddInstruction(parameter_);
     return_block_->AddInstruction(new (&allocator_) HReturnVoid());
     exit_block_->AddInstruction(new (&allocator_) HExit());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index e0c582a..eebc49c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -29,6 +29,7 @@
 #include "base/stl_util.h"
 #include "base/transform_array_ref.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "handle.h"
 #include "handle_scope.h"
@@ -800,7 +801,7 @@
   }
 
   // Catch block information constructor.
-  TryCatchInformation(uint16_t catch_type_index, const DexFile& dex_file)
+  TryCatchInformation(dex::TypeIndex catch_type_index, const DexFile& dex_file)
       : try_entry_(nullptr),
         catch_dex_file_(&dex_file),
         catch_type_index_(catch_type_index) {}
@@ -816,10 +817,10 @@
 
   bool IsCatchAllTypeIndex() const {
     DCHECK(IsCatchBlock());
-    return catch_type_index_ == DexFile::kDexNoIndex16;
+    return !catch_type_index_.IsValid();
   }
 
-  uint16_t GetCatchTypeIndex() const {
+  dex::TypeIndex GetCatchTypeIndex() const {
     DCHECK(IsCatchBlock());
     return catch_type_index_;
   }
@@ -836,7 +837,7 @@
 
   // Exception type information. Only set for catch blocks.
   const DexFile* catch_dex_file_;
-  const uint16_t catch_type_index_;
+  const dex::TypeIndex catch_type_index_;
 };
 
 static constexpr size_t kNoLifetime = -1;
@@ -3671,7 +3672,7 @@
   HNewInstance(HInstruction* cls,
                HCurrentMethod* current_method,
                uint32_t dex_pc,
-               uint16_t type_index,
+               dex::TypeIndex type_index,
                const DexFile& dex_file,
                bool needs_access_check,
                bool finalizable,
@@ -3686,7 +3687,7 @@
     SetRawInputAt(1, current_method);
   }
 
-  uint16_t GetTypeIndex() const { return type_index_; }
+  dex::TypeIndex GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
   // Calls runtime so needs an environment.
@@ -3719,7 +3720,7 @@
   static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
 
-  const uint16_t type_index_;
+  const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
   QuickEntrypointEnum entrypoint_;
 
@@ -4265,7 +4266,7 @@
   HNewArray(HInstruction* length,
             HCurrentMethod* current_method,
             uint32_t dex_pc,
-            uint16_t type_index,
+            dex::TypeIndex type_index,
             const DexFile& dex_file,
             QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
@@ -4276,7 +4277,7 @@
     SetRawInputAt(1, current_method);
   }
 
-  uint16_t GetTypeIndex() const { return type_index_; }
+  dex::TypeIndex GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
   // Calls runtime so needs an environment.
@@ -4292,7 +4293,7 @@
   DECLARE_INSTRUCTION(NewArray);
 
  private:
-  const uint16_t type_index_;
+  const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
   const QuickEntrypointEnum entrypoint_;
 
@@ -4829,7 +4830,7 @@
 class HParameterValue FINAL : public HExpression<0> {
  public:
   HParameterValue(const DexFile& dex_file,
-                  uint16_t type_index,
+                  dex::TypeIndex type_index,
                   uint8_t index,
                   Primitive::Type parameter_type,
                   bool is_this = false)
@@ -4842,7 +4843,7 @@
   }
 
   const DexFile& GetDexFile() const { return dex_file_; }
-  uint16_t GetTypeIndex() const { return type_index_; }
+  dex::TypeIndex GetTypeIndex() const { return type_index_; }
   uint8_t GetIndex() const { return index_; }
   bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); }
 
@@ -4860,7 +4861,7 @@
                 "Too many packed fields.");
 
   const DexFile& dex_file_;
-  const uint16_t type_index_;
+  const dex::TypeIndex type_index_;
   // The index of this parameter in the parameters list. Must be less
   // than HGraph::number_of_in_vregs_.
   const uint8_t index_;
@@ -5455,7 +5456,7 @@
   };
 
   HLoadClass(HCurrentMethod* current_method,
-             uint16_t type_index,
+             dex::TypeIndex type_index,
              const DexFile& dex_file,
              bool is_referrers_class,
              uint32_t dex_pc,
@@ -5487,7 +5488,7 @@
 
   void SetLoadKindWithTypeReference(LoadKind load_kind,
                                     const DexFile& dex_file,
-                                    uint32_t type_index) {
+                                    dex::TypeIndex type_index) {
     DCHECK(HasTypeReference(load_kind));
     DCHECK(IsSameDexFile(dex_file_, dex_file));
     DCHECK_EQ(type_index_, type_index);
@@ -5511,7 +5512,7 @@
 
   bool InstructionDataEquals(const HInstruction* other) const;
 
-  size_t ComputeHashCode() const OVERRIDE { return type_index_; }
+  size_t ComputeHashCode() const OVERRIDE { return type_index_.index_; }
 
   bool CanBeNull() const OVERRIDE { return false; }
 
@@ -5547,7 +5548,7 @@
     loaded_class_rti_ = rti;
   }
 
-  uint32_t GetTypeIndex() const { return type_index_; }
+  dex::TypeIndex GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
   uint32_t GetDexCacheElementOffset() const;
@@ -5630,7 +5631,7 @@
   // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
   HUserRecord<HInstruction*> special_input_;
 
-  const uint16_t type_index_;
+  const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
 
   union {
@@ -5690,7 +5691,10 @@
     // all other types are unavailable.
     kDexCacheViaMethod,
 
-    kLast = kDexCacheViaMethod
+    // Load from the root table associated with the JIT compiled method.
+    kJitTableAddress,
+
+    kLast = kJitTableAddress,
   };
 
   HLoadString(HCurrentMethod* current_method,
@@ -5748,7 +5752,8 @@
     LoadKind load_kind = GetLoadKind();
     if (load_kind == LoadKind::kBootImageLinkTimeAddress ||
         load_kind == LoadKind::kBootImageLinkTimePcRelative ||
-        load_kind == LoadKind::kBootImageAddress) {
+        load_kind == LoadKind::kBootImageAddress ||
+        load_kind == LoadKind::kJitTableAddress) {
       return false;
     }
     return !IsInDexCache();
@@ -5801,7 +5806,8 @@
     return load_kind == LoadKind::kBootImageLinkTimeAddress ||
         load_kind == LoadKind::kBootImageLinkTimePcRelative ||
         load_kind == LoadKind::kBssEntry ||
-        load_kind == LoadKind::kDexCacheViaMethod;
+        load_kind == LoadKind::kDexCacheViaMethod ||
+        load_kind == LoadKind::kJitTableAddress;
   }
 
   static bool HasAddress(LoadKind load_kind) {
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index d4e2a58..5d9a652 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -35,7 +35,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
   entry->AddInstruction(new (&allocator) HGoto());
 
@@ -78,9 +78,9 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter1 = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   HInstruction* parameter2 = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter1);
   entry->AddInstruction(parameter2);
   entry->AddInstruction(new (&allocator) HExit());
@@ -106,7 +106,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   ASSERT_FALSE(parameter->HasUses());
@@ -127,7 +127,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter1 = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   HInstruction* with_environment = new (&allocator) HNullCheck(parameter1, 0);
   entry->AddInstruction(parameter1);
   entry->AddInstruction(with_environment);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 7a930cc..2382b72 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -61,6 +61,7 @@
 #include "debug/method_debug_info.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
+#include "dex_file_types.h"
 #include "driver/compiler_driver-inl.h"
 #include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
@@ -117,6 +118,7 @@
 
   size_t GetSize() const { return size_; }
   const ArenaVector<uint8_t>& GetMemory() const { return memory_; }
+  uint8_t* GetData() { return memory_.data(); }
 
  private:
   ArenaVector<uint8_t> memory_;
@@ -624,17 +626,14 @@
   UNUSED(codegen);  // To avoid compilation error when compiling for svelte
   OptimizingCompilerStats* stats = compilation_stats_.get();
   ArenaAllocator* arena = graph->GetArena();
-#ifdef ART_USE_VIXL_ARM_BACKEND
-  UNUSED(arena);
-  UNUSED(pass_observer);
-  UNUSED(stats);
-#endif
   switch (instruction_set) {
-#if defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_USE_VIXL_ARM_BACKEND)
+#if defined(ART_ENABLE_CODEGEN_arm)
     case kThumb2:
     case kArm: {
+#ifndef ART_USE_VIXL_ARM_BACKEND
       arm::DexCacheArrayFixups* fixups =
           new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
+#endif
       arm::InstructionSimplifierArm* simplifier =
           new (arena) arm::InstructionSimplifierArm(graph, stats);
       SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
@@ -643,7 +642,9 @@
         simplifier,
         side_effects,
         gvn,
+#ifndef ART_USE_VIXL_ARM_BACKEND
         fixups
+#endif
       };
       RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer);
       break;
@@ -948,7 +949,7 @@
     graph->SetArtMethod(method);
     ScopedObjectAccess soa(Thread::Current());
     interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize());
-    uint16_t type_index = method->GetDeclaringClass()->GetDexTypeIndex();
+    dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex();
 
     // Update the dex cache if the type is not in it yet. Note that under AOT,
     // the verifier must have set it, but under JIT, there's no guarantee, as we
@@ -1125,7 +1126,7 @@
                                     jit::JitCodeCache* code_cache,
                                     ArtMethod* method,
                                     bool osr) {
-  StackHandleScope<2> hs(self);
+  StackHandleScope<3> hs(self);
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
       method->GetDeclaringClass()->GetClassLoader()));
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
@@ -1171,25 +1172,46 @@
   }
 
   size_t stack_map_size = codegen->ComputeStackMapsSize();
-  uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size, method);
-  if (stack_map_data == nullptr) {
+  size_t number_of_roots = codegen->GetNumberOfJitRoots();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  // We allocate an object array to ensure the JIT roots that we will collect in EmitJitRoots
+  // will be visible by the GC between EmitLiterals and CommitCode. Once CommitCode is
+  // executed, this array is not needed.
+  Handle<mirror::ObjectArray<mirror::Object>> roots(
+      hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
+          self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots)));
+  if (roots.Get() == nullptr) {
+    // Out of memory, just clear the exception to avoid any Java exception uncaught problems.
+    DCHECK(self->IsExceptionPending());
+    self->ClearException();
+    return false;
+  }
+  uint8_t* stack_map_data = nullptr;
+  uint8_t* roots_data = nullptr;
+  code_cache->ReserveData(
+      self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data);
+  if (stack_map_data == nullptr || roots_data == nullptr) {
     return false;
   }
   MaybeRecordStat(MethodCompilationStat::kCompiled);
   codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), *code_item);
+  codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data, dex_cache);
+
   const void* code = code_cache->CommitCode(
       self,
       method,
       stack_map_data,
+      roots_data,
       codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
       codegen->GetCoreSpillMask(),
       codegen->GetFpuSpillMask(),
       code_allocator.GetMemory().data(),
       code_allocator.GetSize(),
-      osr);
+      osr,
+      roots);
 
   if (code == nullptr) {
-    code_cache->ClearData(self, stack_map_data);
+    code_cache->ClearData(self, stack_map_data, roots_data);
     return false;
   }
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 0db6088..f9ac3a0 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -143,7 +143,7 @@
   // - or the load class has only one use.
   if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) {
     instruction->SetEntrypoint(kQuickAllocObject);
-    instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0);
+    instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex().index_), 0);
     if (has_only_one_use) {
       // We've just removed the only use of the HLoadClass. Since we don't run DCE after this pass,
       // do it manually if possible.
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index d588dea..c191c66 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -96,7 +96,7 @@
   void VisitBoundType(HBoundType* instr) OVERRIDE;
   void VisitNullCheck(HNullCheck* instr) OVERRIDE;
   void UpdateReferenceTypeInfo(HInstruction* instr,
-                               uint16_t type_idx,
+                               dex::TypeIndex type_idx,
                                const DexFile& dex_file,
                                bool is_exact);
 
@@ -463,7 +463,7 @@
 }
 
 void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
-                                                                   uint16_t type_idx,
+                                                                   dex::TypeIndex type_idx,
                                                                    const DexFile& dex_file,
                                                                    bool is_exact) {
   DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
@@ -484,7 +484,7 @@
 
 static mirror::Class* GetClassFromDexCache(Thread* self,
                                            const DexFile& dex_file,
-                                           uint16_t type_idx,
+                                           dex::TypeIndex type_idx,
                                            Handle<mirror::DexCache> hint_dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache);
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 55ea99e..559f409 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -20,6 +20,7 @@
 #include "code_generator.h"
 #include "code_generator_x86.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "dex_instruction.h"
 #include "driver/compiler_options.h"
 #include "nodes.h"
@@ -495,7 +496,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HBasicBlock* block = new (allocator) HBasicBlock(graph);
@@ -658,7 +659,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimNot);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
   entry->AddInstruction(parameter);
 
   HBasicBlock* block = new (allocator) HBasicBlock(graph);
@@ -742,7 +743,7 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* parameter = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   entry->AddInstruction(parameter);
 
   HInstruction* constant1 = graph->GetIntConstant(1);
@@ -821,9 +822,9 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* first = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   HInstruction* second = new (allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   entry->AddInstruction(first);
   entry->AddInstruction(second);
 
@@ -883,13 +884,13 @@
   graph->AddBlock(entry);
   graph->SetEntryBlock(entry);
   HInstruction* one = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   HInstruction* two = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   HInstruction* three = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   HInstruction* four = new (&allocator) HParameterValue(
-      graph->GetDexFile(), 0, 0, Primitive::kPrimInt);
+      graph->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt);
   entry->AddInstruction(one);
   entry->AddInstruction(two);
   entry->AddInstruction(three);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 63e4ca6..a127708 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -147,7 +147,7 @@
   DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening.";
 
   const DexFile& dex_file = load_class->GetDexFile();
-  uint32_t type_index = load_class->GetTypeIndex();
+  dex::TypeIndex type_index = load_class->GetTypeIndex();
 
   bool is_in_dex_cache = false;
   bool is_in_boot_image = false;
@@ -197,7 +197,7 @@
           // inlined frames are used correctly for OOM stack trace.
           // TODO: Write a test for this. Bug: 29416588
           desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
-          void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
+          void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index.index_];
           address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
         }
         // AOT app compilation. Check if the class is in the boot image.
@@ -281,7 +281,8 @@
         : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
 
     if (codegen_->GetCompilerOptions().IsBootImage()) {
-      // Compiling boot image. Resolve the string and allocate it if needed.
+      // Compiling boot image. Resolve the string and allocate it if needed, to ensure
+      // the string will be added to the boot image.
       DCHECK(!runtime->UseJitCompilation());
       mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
       CHECK(string != nullptr);
@@ -297,10 +298,14 @@
     } else if (runtime->UseJitCompilation()) {
       // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
       // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
-      mirror::String* string = dex_cache->GetResolvedString(string_index);
-      if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
-        desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
-        address = reinterpret_cast64<uint64_t>(string);
+      mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
+      if (string != nullptr) {
+        if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+          desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+          address = reinterpret_cast64<uint64_t>(string);
+        } else {
+          desired_load_kind = HLoadString::LoadKind::kJitTableAddress;
+        }
       }
     } else {
       // AOT app compilation. Try to lookup the string without allocating if not found.
@@ -322,6 +327,7 @@
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
     case HLoadString::LoadKind::kBssEntry:
     case HLoadString::LoadKind::kDexCacheViaMethod:
+    case HLoadString::LoadKind::kJitTableAddress:
       load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index);
       break;
     case HLoadString::LoadKind::kBootImageAddress:
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index e3b9fb6..c35c393 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -43,12 +43,12 @@
 }
 
 const uint8_t* ArmVIXLAssembler::CodeBufferBaseAddress() const {
-  return vixl_masm_.GetStartAddress<uint8_t*>();
+  return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
 }
 
 void ArmVIXLAssembler::FinalizeInstructions(const MemoryRegion& region) {
   // Copy the instructions from the buffer.
-  MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+  MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
   region.CopyFrom(0, from);
 }
 
@@ -365,7 +365,7 @@
       if (stack_offset != 0) {
         base = temps.Acquire();
         DCHECK_EQ(regs & (1u << base.GetCode()), 0u);
-        ___ Add(base, sp, stack_offset);
+        ___ Add(base, sp, Operand::From(stack_offset));
       }
       ___ Stm(base, NO_WRITE_BACK, RegisterList(regs));
     } else {
@@ -385,7 +385,7 @@
       vixl32::Register base = sp;
       if (stack_offset != 0) {
         base = temps.Acquire();
-        ___ Add(base, sp, stack_offset);
+        ___ Add(base, sp, Operand::From(stack_offset));
       }
       ___ Ldm(base, NO_WRITE_BACK, RegisterList(regs));
     } else {
@@ -429,5 +429,31 @@
   }
 }
 
+void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn,
+                                                   vixl32::Label* label,
+                                                   bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    // In T32, Cbz/Cbnz instructions have following limitations:
+    // - There are only 7 bits (i:imm5:0) to encode branch target address (cannot be far target).
+    // - Only low registers (i.e R0 .. R7) can be encoded.
+    // - Only forward branches (unbound labels) are supported.
+    Cbz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(eq, label);
+}
+
+void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn,
+                                                      vixl32::Label* label,
+                                                      bool is_far_target) {
+  if (!is_far_target && rn.IsLow() && !label->IsBound()) {
+    Cbnz(rn, label);
+    return;
+  }
+  Cmp(rn, 0);
+  B(ne, label);
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index e020628..b4a4abc 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,6 +37,25 @@
 namespace art {
 namespace arm {
 
+class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler {
+ public:
+  // The following interfaces can generate CMP+Bcc or Cbz/Cbnz.
+  // CMP+Bcc are generated by default.
+  // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz,
+  // then Cbz/Cbnz is generated.
+  // Prefer following interfaces to using vixl32::MacroAssembler::Cbz/Cbnz.
+  // In T32, Cbz/Cbnz instructions have following limitations:
+  // - Far targets, which are over 126 bytes away, are not supported.
+  // - Only low registers can be encoded.
+  // - Backward branches are not supported.
+  void CompareAndBranchIfZero(vixl32::Register rn,
+                              vixl32::Label* label,
+                              bool is_far_target = true);
+  void CompareAndBranchIfNonZero(vixl32::Register rn,
+                                 vixl32::Label* label,
+                                 bool is_far_target = true);
+};
+
 class ArmVIXLAssembler FINAL : public Assembler {
  private:
   class ArmException;
@@ -48,7 +67,7 @@
   }
 
   virtual ~ArmVIXLAssembler() {}
-  vixl32::MacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
+  ArmVIXLMacroAssembler* GetVIXLAssembler() { return &vixl_masm_; }
   void FinalizeCode() OVERRIDE;
 
   // Size of generated code.
@@ -117,7 +136,7 @@
 
  private:
   // VIXL assembler.
-  vixl32::MacroAssembler vixl_masm_;
+  ArmVIXLMacroAssembler vixl_masm_;
 };
 
 // Thread register declaration.
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
index cf7a4d1..3f425df 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm.cc
@@ -594,6 +594,41 @@
   __ b(slow->Entry(), NE);
 }
 
+std::unique_ptr<JNIMacroLabel> ArmJNIMacroAssembler::CreateLabel() {
+  return std::unique_ptr<JNIMacroLabel>(new ArmJNIMacroLabel());
+}
+
+void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label) {
+  CHECK(label != nullptr);
+  __ b(ArmJNIMacroLabel::Cast(label)->AsArm());
+}
+
+void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label,
+                                JNIMacroUnaryCondition condition,
+                                ManagedRegister test) {
+  CHECK(label != nullptr);
+
+  arm::Condition arm_cond;
+  switch (condition) {
+    case JNIMacroUnaryCondition::kZero:
+      arm_cond = EQ;
+      break;
+    case JNIMacroUnaryCondition::kNotZero:
+      arm_cond = NE;
+      break;
+    default:
+      LOG(FATAL) << "Not implemented condition: " << static_cast<int>(condition);
+      UNREACHABLE();
+  }
+  __ cmp(test.AsArm().AsCoreRegister(), ShifterOperand(0));
+  __ b(ArmJNIMacroLabel::Cast(label)->AsArm(), arm_cond);
+}
+
+void ArmJNIMacroAssembler::Bind(JNIMacroLabel* label) {
+  CHECK(label != nullptr);
+  __ Bind(ArmJNIMacroLabel::Cast(label)->AsArm());
+}
+
 #undef __
 
 void ArmExceptionSlowPath::Emit(Assembler* sasm) {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.h b/compiler/utils/arm/jni_macro_assembler_arm.h
index 4471906..809ac8b 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm.h
@@ -25,6 +25,7 @@
 #include "base/enums.h"
 #include "base/macros.h"
 #include "utils/jni_macro_assembler.h"
+#include "utils/label.h"
 #include "offsets.h"
 
 namespace art {
@@ -159,10 +160,26 @@
 
   void MemoryBarrier(ManagedRegister scratch) OVERRIDE;
 
+  // Create a new label that can be used with Jump/Bind calls.
+  std::unique_ptr<JNIMacroLabel> CreateLabel() OVERRIDE;
+  // Emit an unconditional jump to the label.
+  void Jump(JNIMacroLabel* label) OVERRIDE;
+  // Emit a conditional jump to the label by applying a unary condition test to the register.
+  void Jump(JNIMacroLabel* label, JNIMacroUnaryCondition cond, ManagedRegister test) OVERRIDE;
+  // Code at this offset will serve as the target for the Jump call.
+  void Bind(JNIMacroLabel* label) OVERRIDE;
+
  private:
   std::unique_ptr<ArmAssembler> asm_;
 };
 
+class ArmJNIMacroLabel FINAL : public JNIMacroLabelCommon<ArmJNIMacroLabel, art::Label, kArm> {
+ public:
+  art::Label* AsArm() {
+    return AsPlatformLabel();
+  }
+};
+
 }  // namespace arm
 }  // namespace art
 
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 8a9fd90..f20ed0a 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -49,7 +49,7 @@
   return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode()));
 }
 
-static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);;
+static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);
 
 void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size,
                                           ManagedRegister method_reg,
@@ -168,6 +168,8 @@
     CHECK_EQ(0u, size);
   } else if (src.IsCoreRegister()) {
     CHECK_EQ(4u, size);
+    UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+    temps.Exclude(src.AsVIXLRegister());
     asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
   } else if (src.IsRegisterPair()) {
     CHECK_EQ(8u, size);
@@ -186,12 +188,16 @@
 void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
   ArmManagedRegister src = msrc.AsArm();
   CHECK(src.IsCoreRegister()) << src;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(src.AsVIXLRegister());
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
 }
 
 void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
   ArmManagedRegister src = msrc.AsArm();
   CHECK(src.IsCoreRegister()) << src;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(src.AsVIXLRegister());
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
 }
 
@@ -202,6 +208,8 @@
   ArmManagedRegister src = msrc.AsArm();
   ArmManagedRegister scratch = mscratch.AsArm();
   asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value());
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4);
 }
@@ -210,6 +218,8 @@
                                        FrameOffset src,
                                        ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value());
 }
@@ -220,6 +230,8 @@
                                        bool unpoison_reference) {
   ArmManagedRegister dst = dest.AsArm();
   CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord,
                       dst.AsVIXLRegister(),
                       base.AsArm().AsVIXLRegister(),
@@ -246,6 +258,8 @@
                                                      ManagedRegister scratch) {
   ArmManagedRegister mscratch = scratch.AsArm();
   CHECK(mscratch.IsCoreRegister()) << mscratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(mscratch.AsVIXLRegister());
   asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm);
   asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value());
 }
@@ -263,6 +277,8 @@
 void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
   ArmManagedRegister dst = m_dst.AsArm();
   CHECK(dst.IsCoreRegister()) << dst;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(dst.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value());
 }
 
@@ -271,6 +287,8 @@
                                                     ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
 }
@@ -286,6 +304,8 @@
                                                         ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value());
   asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value());
 }
@@ -312,6 +332,8 @@
   if (!dst.Equals(src)) {
     if (dst.IsCoreRegister()) {
       CHECK(src.IsCoreRegister()) << src;
+      UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+      temps.Exclude(dst.AsVIXLRegister());
       ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister());
     } else if (dst.IsDRegister()) {
       if (src.IsDRegister()) {
@@ -351,6 +373,8 @@
   ArmManagedRegister temp = scratch.AsArm();
   CHECK(temp.IsCoreRegister()) << temp;
   CHECK(size == 4 || size == 8) << size;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(temp.AsVIXLRegister());
   if (size == 4) {
     asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value());
     asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value());
@@ -414,6 +438,8 @@
   ArmManagedRegister in_reg = min_reg.AsArm();
   CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
   CHECK(out_reg.IsCoreRegister()) << out_reg;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(out_reg.AsVIXLRegister());
   if (null_allowed) {
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
     // the address in the handle scope holding the reference.
@@ -425,6 +451,8 @@
                           handle_scope_offset.Int32Value());
       in_reg = out_reg;
     }
+
+    temps.Exclude(in_reg.AsVIXLRegister());
     ___ Cmp(in_reg.AsVIXLRegister(), 0);
 
     if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
@@ -457,6 +485,8 @@
                                                       bool null_allowed) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   if (null_allowed) {
     asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value());
     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
@@ -503,6 +533,8 @@
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(base.IsCoreRegister()) << base;
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   asm_.LoadFromOffset(kLoadWord,
                       scratch.AsVIXLRegister(),
                       base.AsVIXLRegister(),
@@ -514,6 +546,8 @@
 void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(scratch.IsCoreRegister()) << scratch;
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   // Call *(*(SP + base) + offset)
   asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value());
   asm_.LoadFromOffset(kLoadWord,
@@ -541,6 +575,8 @@
 void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
   CHECK_ALIGNED(stack_adjust, kStackAlignment);
   ArmManagedRegister scratch = m_scratch.AsArm();
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(scratch.AsVIXLRegister());
   exception_blocks_.emplace_back(
       new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
   asm_.LoadFromOffset(kLoadWord,
@@ -574,10 +610,12 @@
 
   switch (condition) {
     case JNIMacroUnaryCondition::kZero:
-      ___ Cbz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(),
+                                 ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     case JNIMacroUnaryCondition::kNotZero:
-      ___ Cbnz(test.AsArm().AsVIXLRegister(), ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+      ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(),
+                                    ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
       break;
     default:
       LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);
@@ -596,11 +634,14 @@
   if (exception->stack_adjust_ != 0) {  // Fix up the frame.
     DecreaseFrameSize(exception->stack_adjust_);
   }
+
+  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+  temps.Exclude(exception->scratch_.AsVIXLRegister());
   // Pass exception object as argument.
   // Don't care about preserving r0 as this won't return.
   ___ Mov(r0, exception->scratch_.AsVIXLRegister());
+  temps.Include(exception->scratch_.AsVIXLRegister());
   // TODO: check that exception->scratch_ is dead by this point.
-  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   vixl32::Register temp = temps.Acquire();
   ___ Ldr(temp,
           MemOperand(tr,
@@ -622,6 +663,9 @@
   } else if (dest.IsCoreRegister()) {
     CHECK(!dest.AsVIXLRegister().Is(sp)) << dest;
 
+    UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+    temps.Exclude(dest.AsVIXLRegister());
+
     if (size == 1u) {
       ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset));
     } else {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index f91bcfa..6ed0e9b 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -40,12 +40,12 @@
 }
 
 const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const {
-  return vixl_masm_.GetStartAddress<uint8_t*>();
+  return vixl_masm_.GetBuffer().GetStartAddress<const uint8_t*>();
 }
 
 void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) {
   // Copy the instructions from the buffer.
-  MemoryRegion from(vixl_masm_.GetStartAddress<void*>(), CodeSize());
+  MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), CodeSize());
   region.CopyFrom(0, from);
 }
 
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 10bed13..50a1d9f 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1753,7 +1753,10 @@
   __ LoadFromOffset(kLoadWordPair, R2, R4, 0x40400);
   __ LoadFromOffset(kLoadWordPair, R4, R4, 0x40400);
 
+  vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+  temps.Exclude(R12);
   __ LoadFromOffset(kLoadWord, R0, R12, 12);  // 32-bit because of R12.
+  temps.Include(R12);
   __ LoadFromOffset(kLoadWord, R2, R4, 0xa4 - 0x100000);
 
   __ LoadFromOffset(kLoadSignedByte, R2, R4, 12);
@@ -1783,7 +1786,10 @@
   __ StoreToOffset(kStoreWordPair, R2, R4, 0x40400);
   __ StoreToOffset(kStoreWordPair, R4, R4, 0x40400);
 
+  vixl::aarch32::UseScratchRegisterScope temps(assembler.asm_.GetVIXLAssembler());
+  temps.Exclude(R12);
   __ StoreToOffset(kStoreWord, R0, R12, 12);  // 32-bit because of R12.
+  temps.Include(R12);
   __ StoreToOffset(kStoreWord, R2, R4, 0xa4 - 0x100000);
 
   __ StoreToOffset(kStoreByte, R2, R4, 12);
diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index 2f154fb..1b74313 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -20,7 +20,7 @@
 #include <vector>
 
 #ifdef ART_ENABLE_CODEGEN_arm
-#include "arm/jni_macro_assembler_arm_vixl.h"
+#include "arm/jni_macro_assembler_arm.h"
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
 #include "arm64/jni_macro_assembler_arm64.h"
@@ -58,7 +58,7 @@
 #ifdef ART_ENABLE_CODEGEN_arm
     case kArm:
     case kThumb2:
-      return MacroAsm32UniquePtr(new (arena) arm::ArmVIXLJNIMacroAssembler(arena));
+      return MacroAsm32UniquePtr(new (arena) arm::ArmJNIMacroAssembler(arena, instruction_set));
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips
     case kMips:
diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc
index da4ac79..922f8b1 100644
--- a/compiler/utils/test_dex_file_builder_test.cc
+++ b/compiler/utils/test_dex_file_builder_test.cc
@@ -62,7 +62,8 @@
   };
   ASSERT_EQ(arraysize(expected_types), dex_file->NumTypeIds());
   for (size_t i = 0; i != arraysize(expected_types); ++i) {
-    EXPECT_STREQ(expected_types[i], dex_file->GetTypeDescriptor(dex_file->GetTypeId(i))) << i;
+    EXPECT_STREQ(expected_types[i],
+                 dex_file->GetTypeDescriptor(dex_file->GetTypeId(dex::TypeIndex(i)))) << i;
   }
 
   ASSERT_EQ(1u, dex_file->NumFieldIds());
diff --git a/compiler/utils/type_reference.h b/compiler/utils/type_reference.h
index d0c1656..a0fa1a4 100644
--- a/compiler/utils/type_reference.h
+++ b/compiler/utils/type_reference.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "dex_file_types.h"
 #include "string_reference.h"
 
 namespace art {
@@ -28,10 +29,10 @@
 
 // A type is located by its DexFile and the string_ids_ table index into that DexFile.
 struct TypeReference {
-  TypeReference(const DexFile* file, uint32_t index) : dex_file(file), type_index(index) { }
+  TypeReference(const DexFile* file, dex::TypeIndex index) : dex_file(file), type_index(index) { }
 
   const DexFile* dex_file;
-  uint32_t type_index;
+  dex::TypeIndex type_index;
 };
 
 // Compare the actual referenced type names. Used for type reference deduplication.
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 6b690aa..525a2ee 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -23,6 +23,7 @@
 #include "compiler/driver/compiler_driver.h"
 #include "compiler_callbacks.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "handle_scope-inl.h"
 #include "verifier/method_verifier-inl.h"
 #include "mirror/class_loader.h"
@@ -82,10 +83,13 @@
     // The compiler driver handles the verifier deps in the callbacks, so
     // remove what this class did for unit testing.
     verifier_deps_.reset(nullptr);
-    callbacks_->SetVerifierDeps(nullptr);
-    compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings);
+    callbacks_->SetVerifierDeps(deps);
+    compiler_driver_->Verify(class_loader_, dex_files_, &timings);
     // The compiler driver may have updated the VerifierDeps in the callback object.
-    verifier_deps_.reset(callbacks_->GetVerifierDeps());
+    if (callbacks_->GetVerifierDeps() != deps) {
+      verifier_deps_.reset(callbacks_->GetVerifierDeps());
+    }
+    callbacks_->SetVerifierDeps(nullptr);
   }
 
   void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
@@ -153,6 +157,7 @@
     }
     CHECK(method != nullptr);
 
+    Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps());
     MethodVerifier verifier(Thread::Current(),
                             primary_dex_file_,
                             dex_cache_handle,
@@ -168,6 +173,7 @@
                             false /* verify to dump */,
                             true /* allow_thread_suspension */);
     verifier.Verify();
+    Thread::Current()->SetVerifierDeps(nullptr);
     return !verifier.HasFailures();
   }
 
@@ -207,8 +213,8 @@
         hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
     MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
     for (const DexFile* dex_file : dex_files_) {
-      const std::vector<uint16_t>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
-      std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end());
+      const std::vector<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
+      std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
       for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
         const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -228,10 +234,9 @@
   bool HasUnverifiedClass(const std::string& cls) {
     const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
     DCHECK(type_id != nullptr);
-    uint16_t index = primary_dex_file_->GetIndexForTypeId(*type_id);
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+    dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id);
     for (const auto& dex_dep : verifier_deps_->dex_deps_) {
-      for (uint16_t entry : dex_dep.second->unverified_classes_) {
+      for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) {
         if (index == entry) {
           return true;
         }
@@ -245,7 +250,6 @@
   bool HasAssignable(const std::string& expected_destination,
                      const std::string& expected_source,
                      bool expected_is_assignable) {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       const DexFile& dex_file = *dex_dep.first;
       auto& storage = expected_is_assignable ? dex_dep.second->assignable_types_
@@ -267,7 +271,6 @@
   bool HasClass(const std::string& expected_klass,
                 bool expected_resolved,
                 const std::string& expected_access_flags = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       for (auto& entry : dex_dep.second->classes_) {
         if (expected_resolved != entry.IsResolved()) {
@@ -302,7 +305,6 @@
                 bool expected_resolved,
                 const std::string& expected_access_flags = "",
                 const std::string& expected_decl_klass = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       for (auto& entry : dex_dep.second->fields_) {
         if (expected_resolved != entry.IsResolved()) {
@@ -356,7 +358,6 @@
                  bool expected_resolved,
                  const std::string& expected_access_flags = "",
                  const std::string& expected_decl_klass = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_
                           : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_
@@ -405,13 +406,10 @@
   }
 
   size_t NumberOfCompiledDexFiles() {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     return verifier_deps_->dex_deps_.size();
   }
 
   size_t HasEachKindOfRecord() {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
     bool has_strings = false;
     bool has_assignability = false;
     bool has_classes = false;
@@ -462,8 +460,6 @@
   ScopedObjectAccess soa(Thread::Current());
   LoadDexFile(&soa);
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
   uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
   ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds());
   ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1));
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 05a5d0f..0924aec 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -89,6 +89,7 @@
     ],
     static_libs: [
         "libart-compiler",
+        "libart-dexlayout",
         "libart",
         "libvixl-arm",
         "libvixl-arm64",
@@ -118,6 +119,7 @@
     ],
     static_libs: [
         "libartd-compiler",
+        "libartd-dexlayout",
         "libartd",
         "libvixld-arm",
         "libvixld-arm64",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 65703a2..9e6032f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -78,6 +78,7 @@
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
 #include "utils.h"
+#include "vdex_file.h"
 #include "verifier/verifier_deps.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
@@ -270,6 +271,7 @@
                 "|balanced"
                 "|speed-profile"
                 "|speed"
+                "|layout-profile"
                 "|everything-profile"
                 "|everything):");
   UsageError("      select compiler filter.");
@@ -519,6 +521,7 @@
       oat_fd_(-1),
       input_vdex_fd_(-1),
       output_vdex_fd_(-1),
+      input_vdex_file_(nullptr),
       zip_fd_(-1),
       image_base_(0U),
       image_classes_zip_filename_(nullptr),
@@ -709,6 +712,10 @@
       Usage("Output must be supplied with either --oat-file or --oat-fd");
     }
 
+    if (input_vdex_fd_ != -1 && !input_vdex_.empty()) {
+      Usage("Can't have both --input-vdex-fd and --input-vdex");
+    }
+
     if (!oat_filenames_.empty() && oat_fd_ != -1) {
       Usage("--oat-file should not be used with --oat-fd");
     }
@@ -1122,6 +1129,8 @@
         zip_location_ = option.substr(strlen("--zip-location=")).data();
       } else if (option.starts_with("--input-vdex-fd=")) {
         ParseInputVdexFd(option);
+      } else if (option.starts_with("--input-vdex=")) {
+        input_vdex_ = option.substr(strlen("--input-vdex=")).data();
       } else if (option.starts_with("--output-vdex-fd=")) {
         ParseOutputVdexFd(option);
       } else if (option.starts_with("--oat-file=")) {
@@ -1265,6 +1274,17 @@
           return false;
         }
         oat_files_.push_back(std::move(oat_file));
+        DCHECK_EQ(input_vdex_fd_, -1);
+        if (!input_vdex_.empty()) {
+          std::string error_msg;
+          input_vdex_file_.reset(VdexFile::Open(input_vdex_,
+                                                /* writable */ false,
+                                                /* low_4gb */ false,
+                                                &error_msg));
+          if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) {
+            input_vdex_file_.reset(nullptr);
+          }
+        }
 
         DCHECK_EQ(output_vdex_fd_, -1);
         std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
@@ -1292,6 +1312,31 @@
       }
       oat_files_.push_back(std::move(oat_file));
 
+      DCHECK_NE(input_vdex_fd_, output_vdex_fd_);
+      if (input_vdex_fd_ != -1) {
+        struct stat s;
+        int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s));
+        if (rc == -1) {
+          PLOG(WARNING) << "Failed getting length of vdex file";
+        } else {
+          std::string error_msg;
+          input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_,
+                                                s.st_size,
+                                                "vdex",
+                                                /* writable */ false,
+                                                /* low_4gb */ false,
+                                                &error_msg));
+          // If there's any problem with the passed vdex, just warn and proceed
+          // without it.
+          if (input_vdex_file_ == nullptr) {
+            PLOG(WARNING) << "Failed opening vdex file " << error_msg;
+          } else if (!input_vdex_file_->IsValid()) {
+            PLOG(WARNING) << "Existing vdex file is invalid";
+            input_vdex_file_.reset(nullptr);
+          }
+        }
+      }
+
       DCHECK_NE(output_vdex_fd_, -1);
       std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
       std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
@@ -1387,7 +1432,6 @@
   // boot class path.
   bool Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
-    art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
 
     if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
       return false;
@@ -1479,13 +1523,18 @@
         // Unzip or copy dex files straight to the oat file.
         std::unique_ptr<MemMap> opened_dex_files_map;
         std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+        // No need to verify the dex file for:
+        // 1) dexlayout, which already verified it
+        // 2) when we have a vdex file, which means it was already verified.
+        bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile &&
+            (input_vdex_file_ == nullptr);
         if (!oat_writers_[i]->WriteAndOpenDexFiles(
             kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
             rodata_.back(),
             instruction_set_,
             instruction_set_features_.get(),
             key_value_store_.get(),
-            /* verify */ true,
+            verify,
             &opened_dex_files_map,
             &opened_dex_files)) {
           return false;
@@ -1585,6 +1634,9 @@
       dex_caches_.push_back(soa.AddLocalReference<jobject>(
           class_linker->RegisterDexFile(*dex_file,
                                         soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
+      // Pre-register dex files so that we can access verification results without locks during
+      // compilation and verification.
+      verification_results_->PreRegisterDexFile(dex_file);
     }
 
     return true;
@@ -1662,7 +1714,7 @@
                                      swap_fd_,
                                      profile_compilation_info_.get()));
     driver_->SetDexFilesForOatFile(dex_files_);
-    driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_);
+    driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_);
   }
 
   // Notes on the interleaving of creating the images and oat files to
@@ -2235,7 +2287,14 @@
 
   bool AddDexFileSources() {
     TimingLogger::ScopedTiming t2("AddDexFileSources", timings_);
-    if (zip_fd_ != -1) {
+    if (input_vdex_file_ != nullptr) {
+      DCHECK_EQ(oat_writers_.size(), 1u);
+      const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_;
+      DCHECK(!name.empty());
+      if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) {
+        return false;
+      }
+    } else if (zip_fd_ != -1) {
       DCHECK_EQ(oat_writers_.size(), 1u);
       if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false),
                                                     zip_location_.c_str())) {
@@ -2273,7 +2332,9 @@
                                                      compiler_options_.get(),
                                                      oat_file.get()));
       elf_writers_.back()->Start();
-      oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_));
+      bool do_dexlayout = compiler_options_->GetCompilerFilter() == CompilerFilter::kLayoutProfile;
+      oat_writers_.emplace_back(new OatWriter(
+          IsBootImage(), timings_, do_dexlayout ? profile_compilation_info_.get() : nullptr));
     }
   }
 
@@ -2591,6 +2652,8 @@
   int oat_fd_;
   int input_vdex_fd_;
   int output_vdex_fd_;
+  std::string input_vdex_;
+  std::unique_ptr<VdexFile> input_vdex_file_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
   int zip_fd_;
@@ -2787,6 +2850,8 @@
     }
   }
 
+  art::MemMap::Init();  // For ZipEntry::ExtractToMemMap, and vdex.
+
   // Check early that the result of compilation can be written
   if (!dex2oat->OpenFile()) {
     return EXIT_FAILURE;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index fa32178..714a58c 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -24,6 +24,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/stringprintf.h"
+#include "dex_file-inl.h"
 #include "dex2oat_environment_test.h"
 #include "oat.h"
 #include "oat_file.h"
@@ -551,4 +552,107 @@
   RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" });
 }
 
+static const char kDexFileLayoutInputProfile[] = "cHJvADAwMgABAAwAAQABAOqMEeFEZXhOb09hdC5qYXIBAAEA";
+
+static void WriteFileBase64(const char* base64, const char* location) {
+  // Decode base64.
+  CHECK(base64 != nullptr);
+  size_t length;
+  std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
+  CHECK(bytes.get() != nullptr);
+
+  // Write to provided file.
+  std::unique_ptr<File> file(OS::CreateEmptyFile(location));
+  CHECK(file.get() != nullptr);
+  if (!file->WriteFully(bytes.get(), length)) {
+    PLOG(FATAL) << "Failed to write base64 as file";
+  }
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(FATAL) << "Could not flush and close test file.";
+  }
+}
+
+class Dex2oatLayoutTest : public Dex2oatTest {
+ protected:
+  void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED,
+                   CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE {
+    // Ignore, we'll do our own checks.
+  }
+
+  void RunTest() {
+    std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+    std::string profile_location = GetScratchDir() + "/primary.prof";
+    std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+
+    Copy(GetDexSrc2(), dex_location);
+    WriteFileBase64(kDexFileLayoutInputProfile, profile_location.c_str());
+
+    const std::vector<std::string>& extra_args = { "--profile-file=" + profile_location };
+    GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kLayoutProfile, extra_args);
+
+    CheckValidity();
+    ASSERT_TRUE(success_);
+    CheckResult(dex_location, odex_location);
+  }
+  void CheckResult(const std::string& dex_location, const std::string& odex_location) {
+    // Host/target independent checks.
+    std::string error_msg;
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                     odex_location.c_str(),
+                                                     nullptr,
+                                                     nullptr,
+                                                     false,
+                                                     /*low_4gb*/false,
+                                                     dex_location.c_str(),
+                                                     &error_msg));
+    ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+
+    const char* location = dex_location.c_str();
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files));
+    EXPECT_EQ(dex_files.size(), 1U);
+    std::unique_ptr<const DexFile>& old_dex_file = dex_files[0];
+
+    for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+      std::unique_ptr<const DexFile> new_dex_file = oat_dex_file->OpenDexFile(&error_msg);
+      ASSERT_TRUE(new_dex_file != nullptr);
+      uint32_t class_def_count = new_dex_file->NumClassDefs();
+      ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max());
+      ASSERT_GE(class_def_count, 2U);
+
+      // The new layout swaps the classes at indexes 0 and 1.
+      std::string old_class0 = old_dex_file->PrettyType(old_dex_file->GetClassDef(0).class_idx_);
+      std::string old_class1 = old_dex_file->PrettyType(old_dex_file->GetClassDef(1).class_idx_);
+      std::string new_class0 = new_dex_file->PrettyType(new_dex_file->GetClassDef(0).class_idx_);
+      std::string new_class1 = new_dex_file->PrettyType(new_dex_file->GetClassDef(1).class_idx_);
+      EXPECT_EQ(old_class0, new_class1);
+      EXPECT_EQ(old_class1, new_class0);
+    }
+
+    EXPECT_EQ(odex_file->GetCompilerFilter(), CompilerFilter::kLayoutProfile);
+  }
+
+    // Check whether the dex2oat run was really successful.
+    void CheckValidity() {
+      if (kIsTargetBuild) {
+        CheckTargetValidity();
+      } else {
+        CheckHostValidity();
+      }
+    }
+
+    void CheckTargetValidity() {
+      // TODO: Ignore for now.
+    }
+
+    // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+    void CheckHostValidity() {
+      EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+    }
+  };
+
+TEST_F(Dex2oatLayoutTest, TestLayout) {
+  RunTest();
+}
+
 }  // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 30de28e..03d6227 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -45,6 +45,7 @@
 #include "base/stringprintf.h"
 #include "dexdump_cfg.h"
 #include "dex_file-inl.h"
+#include "dex_file_types.h"
 #include "dex_instruction-inl.h"
 
 namespace art {
@@ -482,7 +483,7 @@
     }
     case DexFile::kDexAnnotationType: {
       const u4 str_idx = static_cast<u4>(readVarWidth(data, arg, false));
-      fputs(pDexFile->StringByTypeIdx(str_idx), gOutFile);
+      fputs(pDexFile->StringByTypeIdx(dex::TypeIndex(str_idx)), gOutFile);
       break;
     }
     case DexFile::kDexAnnotationField:
@@ -511,7 +512,7 @@
     }
     case DexFile::kDexAnnotationAnnotation: {
       const u4 type_idx = DecodeUnsignedLeb128(data);
-      fputs(pDexFile->StringByTypeIdx(type_idx), gOutFile);
+      fputs(pDexFile->StringByTypeIdx(dex::TypeIndex(type_idx)), gOutFile);
       // Decode and display all name=value pairs.
       const u4 size = DecodeUnsignedLeb128(data);
       for (u4 i = 0; i < size; i++) {
@@ -592,10 +593,10 @@
   // General class information.
   const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
   fprintf(gOutFile, "Class #%d header:\n", idx);
-  fprintf(gOutFile, "class_idx           : %d\n", pClassDef.class_idx_);
+  fprintf(gOutFile, "class_idx           : %d\n", pClassDef.class_idx_.index_);
   fprintf(gOutFile, "access_flags        : %d (0x%04x)\n",
           pClassDef.access_flags_, pClassDef.access_flags_);
-  fprintf(gOutFile, "superclass_idx      : %d\n", pClassDef.superclass_idx_);
+  fprintf(gOutFile, "superclass_idx      : %d\n", pClassDef.superclass_idx_.index_);
   fprintf(gOutFile, "interfaces_off      : %d (0x%06x)\n",
           pClassDef.interfaces_off_, pClassDef.interfaces_off_);
   fprintf(gOutFile, "source_file_idx     : %d\n", pClassDef.source_file_idx_);
@@ -747,9 +748,8 @@
     const u4 end = start + pTry->insn_count_;
     fprintf(gOutFile, "        0x%04x - 0x%04x\n", start, end);
     for (CatchHandlerIterator it(*pCode, *pTry); it.HasNext(); it.Next()) {
-      const u2 tidx = it.GetHandlerTypeIndex();
-      const char* descriptor =
-          (tidx == DexFile::kDexNoIndex16) ? "<any>" : pDexFile->StringByTypeIdx(tidx);
+      const dex::TypeIndex tidx = it.GetHandlerTypeIndex();
+      const char* descriptor = (!tidx.IsValid()) ? "<any>" : pDexFile->StringByTypeIdx(tidx);
       fprintf(gOutFile, "          %s -> 0x%04x\n", descriptor, it.GetHandlerAddress());
     }  // for
   }  // for
@@ -834,7 +834,7 @@
       break;
     case Instruction::kIndexTypeRef:
       if (index < pDexFile->GetHeader().type_ids_size_) {
-        const char* tp = pDexFile->StringByTypeIdx(index);
+        const char* tp = pDexFile->StringByTypeIdx(dex::TypeIndex(index));
         outSize = snprintf(buf.get(), bufSize, "%s // type@%0*x", tp, width, index);
       } else {
         outSize = snprintf(buf.get(), bufSize, "<type?> // type@%0*x", width, index);
@@ -1461,7 +1461,7 @@
   // General class information.
   char* accessStr = createAccessFlagStr(pClassDef.access_flags_, kAccessForClass);
   const char* superclassDescriptor;
-  if (pClassDef.superclass_idx_ == DexFile::kDexNoIndex16) {
+  if (!pClassDef.superclass_idx_.IsValid()) {
     superclassDescriptor = nullptr;
   } else {
     superclassDescriptor = pDexFile->StringByTypeIdx(pClassDef.superclass_idx_);
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index b9266f7..9ee9ebd 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -12,28 +12,46 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-art_cc_binary {
-    name: "dexlayout",
+art_cc_defaults {
+    name: "libart-dexlayout-defaults",
     host_supported: true,
     srcs: [
-        "dexlayout_main.cc",
         "dexlayout.cc",
         "dex_ir.cc",
         "dex_ir_builder.cc",
         "dex_visualize.cc",
         "dex_writer.cc",
     ],
+    export_include_dirs: ["."],
+    shared_libs: ["libbase"],
+    static_libs: ["libz"],
+}
+
+art_cc_library {
+    name: "libart-dexlayout",
+    defaults: ["libart-dexlayout-defaults"],
+    shared_libs: ["libart"],
+}
+
+art_cc_library {
+    name: "libartd-dexlayout",
+    defaults: ["libart-dexlayout-defaults"],
+    shared_libs: ["libartd"],
+}
+
+art_cc_binary {
+    name: "dexlayout",
+    host_supported: true,
+    srcs: ["dexlayout_main.cc"],
     cflags: ["-Wall"],
     shared_libs: [
         "libart",
-        "libbase",
+        "libart-dexlayout",
     ],
 }
 
 art_cc_test {
     name: "art_dexlayout_tests",
-    defaults: [
-        "art_gtest_defaults",
-    ],
+    defaults: ["art_gtest_defaults"],
     srcs: ["dexlayout_test.cc"],
 }
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index c3c763f..fe2bcce 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -56,6 +56,36 @@
                     entry.end_address_, entry.reg_)));
 }
 
+static uint32_t GetCodeItemSize(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item) {
+  uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&disk_code_item);
+  uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
+  uint32_t tries_size = disk_code_item.tries_size_;
+  if (tries_size == 0) {
+    uintptr_t insns_end = reinterpret_cast<uintptr_t>(&disk_code_item.insns_[insns_size]);
+    return insns_end - code_item_start;
+  } else {
+    uint32_t last_handler_off = 0;
+    for (uint32_t i = 0; i < tries_size; ++i) {
+      // Iterate over the try items to find the last catch handler.
+      const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
+      uint16_t handler_off = disk_try_item->handler_off_;
+      if (handler_off > last_handler_off) {
+        last_handler_off = handler_off;
+      }
+    }
+    // Decode the final handler to see where it ends.
+    const uint8_t* handler_data = DexFile::GetCatchHandlerData(disk_code_item, last_handler_off);
+    int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
+    if (uleb128_count <= 0) {
+      uleb128_count = -uleb128_count + 1;
+    }
+    for (int32_t i = 0; i < uleb128_count; ++i) {
+      DecodeUnsignedLeb128(&handler_data);
+    }
+    return reinterpret_cast<uintptr_t>(handler_data) - code_item_start;
+  }
+}
+
 static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) {
   const uint8_t* stream = debug_info_stream;
   DecodeUnsignedLeb128(&stream);  // line_start
@@ -319,7 +349,7 @@
 }
 
 void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) {
-  const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(i);
+  const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i));
   TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_));
   type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i);
 }
@@ -330,22 +360,22 @@
   TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
 
   ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_),
-                                  GetTypeId(disk_proto_id.return_type_idx_),
+                                  GetTypeId(disk_proto_id.return_type_idx_.index_),
                                   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_),
+  FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_),
+                                  GetTypeId(disk_field_id.type_idx_.index_),
                                   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_),
+  MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
                                      GetProtoId(disk_method_id.proto_idx_),
                                      GetStringId(disk_method_id.name_idx_));
   method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
@@ -353,9 +383,9 @@
 
 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_);
+  const TypeId* class_type = GetTypeId(disk_class_def.class_idx_.index_);
   uint32_t access_flags = disk_class_def.access_flags_;
-  const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
+  const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_.index_);
 
   const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
   TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_);
@@ -384,16 +414,14 @@
   if (dex_type_list == nullptr) {
     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();
-    }
+  auto found_type_list = TypeLists().find(offset);
+  if (found_type_list != TypeLists().end()) {
+    return found_type_list->second.get();
   }
   TypeIdVector* type_vector = new TypeIdVector();
   uint32_t size = dex_type_list->Size();
   for (uint32_t index = 0; index < size; ++index) {
-    type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_));
+    type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_));
   }
   TypeList* new_type_list = new TypeList(type_vector);
   type_lists_.AddItem(new_type_list, offset);
@@ -404,10 +432,9 @@
   if (static_data == nullptr) {
     return nullptr;
   }
-  for (std::unique_ptr<EncodedArrayItem>& existing_array_item : EncodedArrayItems()) {
-    if (existing_array_item->GetOffset() == offset) {
-      return existing_array_item.get();
-    }
+  auto found_encoded_array_item = EncodedArrayItems().find(offset);
+  if (found_encoded_array_item != EncodedArrayItems().end()) {
+    return found_encoded_array_item->second.get();
   }
   uint32_t size = DecodeUnsignedLeb128(&static_data);
   EncodedValueVector* values = new EncodedValueVector();
@@ -422,10 +449,9 @@
 
 AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation,
                                                   uint32_t offset) {
-  for (std::unique_ptr<AnnotationItem>& existing_annotation_item : AnnotationItems()) {
-    if (existing_annotation_item->GetOffset() == offset) {
-      return existing_annotation_item.get();
-    }
+  auto found_annotation_item = AnnotationItems().find(offset);
+  if (found_annotation_item != AnnotationItems().end()) {
+    return found_annotation_item->second.get();
   }
   uint8_t visibility = annotation->visibility_;
   const uint8_t* annotation_data = annotation->annotation_;
@@ -444,10 +470,9 @@
   if (disk_annotations_item.size_ == 0 && offset == 0) {
     return nullptr;
   }
-  for (std::unique_ptr<AnnotationSetItem>& existing_anno_set_item : AnnotationSetItems()) {
-    if (existing_anno_set_item->GetOffset() == offset) {
-      return existing_anno_set_item.get();
-    }
+  auto found_anno_set_item = AnnotationSetItems().find(offset);
+  if (found_anno_set_item != AnnotationSetItems().end()) {
+    return found_anno_set_item->second.get();
   }
   std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>();
   for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
@@ -467,10 +492,9 @@
 
 AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file,
     const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) {
-  for (std::unique_ptr<AnnotationsDirectoryItem>& anno_dir_item : AnnotationsDirectoryItems()) {
-    if (anno_dir_item->GetOffset() == offset) {
-      return anno_dir_item.get();
-    }
+  auto found_anno_dir_item = AnnotationsDirectoryItems().find(offset);
+  if (found_anno_dir_item != AnnotationsDirectoryItems().end()) {
+    return found_anno_dir_item->second.get();
   }
   const DexFile::AnnotationSetItem* class_set_item =
       dex_file.GetClassAnnotationSet(disk_annotations_item);
@@ -535,11 +559,9 @@
     const DexFile& dex_file, MethodId* method_id,
     const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) {
   AnnotationSetRefList* set_ref_list = nullptr;
-  for (std::unique_ptr<AnnotationSetRefList>& existing_set_ref_list : AnnotationSetRefLists()) {
-    if (existing_set_ref_list->GetOffset() == offset) {
-      set_ref_list = existing_set_ref_list.get();
-      break;
-    }
+  auto found_set_ref_list = AnnotationSetRefLists().find(offset);
+  if (found_set_ref_list != AnnotationSetRefLists().end()) {
+    set_ref_list = found_set_ref_list->second.get();
   }
   if (set_ref_list == nullptr) {
     std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>();
@@ -597,8 +619,8 @@
         bool catch_all = false;
         TypeAddrPairVector* addr_pairs = new TypeAddrPairVector();
         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);
+          const dex::TypeIndex type_index = it.GetHandlerTypeIndex();
+          const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_);
           catch_all |= type_id == nullptr;
           addr_pairs->push_back(std::unique_ptr<const TypeAddrPair>(
               new TypeAddrPair(type_id, it.GetHandlerAddress())));
@@ -610,9 +632,10 @@
       tries->push_back(std::unique_ptr<const TryItem>(try_item));
     }
   }
-  // TODO: Calculate the size of the code item.
+  uint32_t size = GetCodeItemSize(dex_file, disk_code_item);
   CodeItem* code_item = new CodeItem(
       registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list);
+  code_item->SetSize(size);
   code_items_.AddItem(code_item, offset);
   // Add "fixup" references to types, strings, methods, and fields.
   // This is temporary, as we will probably want more detailed parsing of the
@@ -690,8 +713,8 @@
       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_data->SetSize(cdii.EndDataPointer() - encoded_data);
     class_datas_.AddItem(class_data, offset);
   }
   return class_data;
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 38eb0b1..a2d1190 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -19,6 +19,7 @@
 #ifndef ART_DEXLAYOUT_DEX_IR_H_
 #define ART_DEXLAYOUT_DEX_IR_H_
 
+#include <map>
 #include <vector>
 #include <stdint.h>
 
@@ -98,34 +99,52 @@
 };
 
 // Collections become owners of the objects added by moving them into unique pointers.
-template<class T> class CollectionWithOffset {
+template<class T> class CollectionBase {
  public:
-  CollectionWithOffset() = default;
-  std::vector<std::unique_ptr<T>>& Collection() { return collection_; }
-  // Read-time support methods
-  void AddItem(T* object, uint32_t offset) {
-    object->SetOffset(offset);
-    collection_.push_back(std::unique_ptr<T>(object));
-  }
+  CollectionBase() = default;
+
+  uint32_t GetOffset() const { return offset_; }
+  void SetOffset(uint32_t new_offset) { offset_ = new_offset; }
+
+ private:
+  uint32_t offset_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(CollectionBase);
+};
+
+template<class T> class CollectionVector : public CollectionBase<T> {
+ public:
+  CollectionVector() = default;
+
   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) {
-    // TODO(sehr): add ordered insertion support.
-    UNIMPLEMENTED(FATAL) << "Insertion not ready";
-  }
-  uint32_t GetOffset() const { return offset_; }
-  void SetOffset(uint32_t new_offset) { offset_ = new_offset; }
   uint32_t Size() const { return collection_.size(); }
+  std::vector<std::unique_ptr<T>>& Collection() { return collection_; }
 
  private:
   std::vector<std::unique_ptr<T>> collection_;
-  uint32_t offset_ = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(CollectionWithOffset);
+  DISALLOW_COPY_AND_ASSIGN(CollectionVector);
+};
+
+template<class T> class CollectionMap : public CollectionBase<T> {
+ public:
+  CollectionMap() = default;
+
+  void AddItem(T* object, uint32_t offset) {
+    object->SetOffset(offset);
+    collection_.emplace(offset, std::unique_ptr<T>(object));
+  }
+  uint32_t Size() const { return collection_.size(); }
+  std::map<uint32_t, std::unique_ptr<T>>& Collection() { return collection_; }
+
+ private:
+  std::map<uint32_t, std::unique_ptr<T>> collection_;
+
+  DISALLOW_COPY_AND_ASSIGN(CollectionMap);
 };
 
 class Collections {
@@ -138,22 +157,23 @@
   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<StringData>>& StringDatas() { return string_datas_.Collection(); }
-  std::vector<std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); }
-  std::vector<std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems()
+  std::map<uint32_t, std::unique_ptr<StringData>>& StringDatas()
+      { return string_datas_.Collection(); }
+  std::map<uint32_t, std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); }
+  std::map<uint32_t, std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems()
       { return encoded_array_items_.Collection(); }
-  std::vector<std::unique_ptr<AnnotationItem>>& AnnotationItems()
+  std::map<uint32_t, std::unique_ptr<AnnotationItem>>& AnnotationItems()
       { return annotation_items_.Collection(); }
-  std::vector<std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems()
+  std::map<uint32_t, std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems()
       { return annotation_set_items_.Collection(); }
-  std::vector<std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists()
+  std::map<uint32_t, std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists()
       { return annotation_set_ref_lists_.Collection(); }
-  std::vector<std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems()
+  std::map<uint32_t, std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems()
       { return annotations_directory_items_.Collection(); }
-  std::vector<std::unique_ptr<DebugInfoItem>>& DebugInfoItems()
+  std::map<uint32_t, std::unique_ptr<DebugInfoItem>>& DebugInfoItems()
       { return debug_info_items_.Collection(); }
-  std::vector<std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); }
-  std::vector<std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); }
+  std::map<uint32_t, std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); }
+  std::map<uint32_t, std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); }
 
   void CreateStringId(const DexFile& dex_file, uint32_t i);
   void CreateTypeId(const DexFile& dex_file, uint32_t i);
@@ -204,7 +224,7 @@
   uint32_t DebugInfoItemsOffset() const { return debug_info_items_.GetOffset(); }
   uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); }
   uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); }
-  uint32_t MapItemOffset() const { return map_item_offset_; }
+  uint32_t MapListOffset() const { return map_list_offset_; }
 
   void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); }
   void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); }
@@ -226,7 +246,7 @@
   void SetDebugInfoItemsOffset(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); }
-  void SetMapItemOffset(uint32_t new_offset) { map_item_offset_ = new_offset; }
+  void SetMapListOffset(uint32_t new_offset) { map_list_offset_ = new_offset; }
 
   uint32_t StringIdsSize() const { return string_ids_.Size(); }
   uint32_t TypeIdsSize() const { return type_ids_.Size(); }
@@ -254,25 +274,25 @@
       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_;
+  CollectionVector<StringId> string_ids_;
+  CollectionVector<TypeId> type_ids_;
+  CollectionVector<ProtoId> proto_ids_;
+  CollectionVector<FieldId> field_ids_;
+  CollectionVector<MethodId> method_ids_;
+  CollectionVector<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_;
+  CollectionMap<StringData> string_datas_;
+  CollectionMap<TypeList> type_lists_;
+  CollectionMap<EncodedArrayItem> encoded_array_items_;
+  CollectionMap<AnnotationItem> annotation_items_;
+  CollectionMap<AnnotationSetItem> annotation_set_items_;
+  CollectionMap<AnnotationSetRefList> annotation_set_ref_lists_;
+  CollectionMap<AnnotationsDirectoryItem> annotations_directory_items_;
+  CollectionMap<DebugInfoItem> debug_info_items_;
+  CollectionMap<CodeItem> code_items_;
+  CollectionMap<ClassData> class_datas_;
 
-  uint32_t map_item_offset_ = 0;
+  uint32_t map_list_offset_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(Collections);
 };
@@ -539,20 +559,20 @@
 
 class MethodItem : public Item {
  public:
-  MethodItem(uint32_t access_flags, const MethodId* method_id, const CodeItem* code)
+  MethodItem(uint32_t access_flags, const MethodId* method_id, CodeItem* code)
       : access_flags_(access_flags), method_id_(method_id), code_(code) { }
   ~MethodItem() OVERRIDE { }
 
   uint32_t GetAccessFlags() const { return access_flags_; }
   const MethodId* GetMethodId() const { return method_id_; }
-  const CodeItem* GetCodeItem() const { return code_; }
+  CodeItem* GetCodeItem() { return code_; }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
   uint32_t access_flags_;
   const MethodId* method_id_;
-  const CodeItem* code_;  // This can be nullptr.
+  CodeItem* code_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(MethodItem);
 };
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 68ff2a2..d0c5bf9 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -71,7 +71,7 @@
     collections.CreateClassDef(dex_file, i);
   }
   // MapItem.
-  collections.SetMapItemOffset(disk_header.map_off_);
+  collections.SetMapListOffset(disk_header.map_off_);
 
   CheckAndSetRemainingOffsets(dex_file, &collections);
 
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index bc9ca6d..02274b2 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -263,11 +263,13 @@
     DumpStringId(method_id->Name(), class_index);
   }
 
-  void DumpMethodItem(const dex_ir::MethodItem* method, const DexFile* dex_file, int class_index) {
-    if (profile_info_ != nullptr) {
+  void DumpMethodItem(dex_ir::MethodItem* method,
+                      const DexFile* dex_file,
+                      int class_index,
+                      ProfileCompilationInfo* profile_info) {
+    if (profile_info != nullptr) {
       uint32_t method_idx = method->GetMethodId()->GetIndex();
-      MethodReference mr(dex_file, method_idx);
-      if (!profile_info_->ContainsMethod(mr)) {
+      if (!profile_info->ContainsMethod(MethodReference(dex_file, method_idx))) {
         return;
       }
     }
@@ -344,13 +346,17 @@
  * Dumps a gnuplot data file showing the parts of the dex_file that belong to each class.
  * If profiling information is present, it dumps only those classes that are marked as hot.
  */
-void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t dex_file_index) {
+void VisualizeDexLayout(dex_ir::Header* header,
+                        const DexFile* dex_file,
+                        size_t dex_file_index,
+                        ProfileCompilationInfo* profile_info) {
   std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections(), dex_file_index));
 
   const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
   for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) {
     dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index);
-    if (profile_info_ != nullptr && !profile_info_->ContainsClass(*dex_file, class_index)) {
+    dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
+    if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) {
       continue;
     }
     dumper->DumpAddressRange(class_def, class_index);
@@ -383,12 +389,12 @@
       }
       if (class_data->DirectMethods()) {
         for (auto& method_item : *class_data->DirectMethods()) {
-          dumper->DumpMethodItem(method_item.get(), dex_file, class_index);
+          dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
         }
       }
       if (class_data->VirtualMethods()) {
         for (auto& method_item : *class_data->VirtualMethods()) {
-          dumper->DumpMethodItem(method_item.get(), dex_file, class_index);
+          dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
         }
       }
     }
diff --git a/dexlayout/dex_visualize.h b/dexlayout/dex_visualize.h
index b1d2ed7..09f8306 100644
--- a/dexlayout/dex_visualize.h
+++ b/dexlayout/dex_visualize.h
@@ -28,11 +28,15 @@
 namespace art {
 
 class DexFile;
+class ProfileCompilationInfo;
 namespace dex_ir {
 class Header;
 }  // namespace dex_ir
 
-void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t dex_file_index);
+void VisualizeDexLayout(dex_ir::Header* header,
+                        const DexFile* dex_file,
+                        size_t dex_file_index,
+                        ProfileCompilationInfo* profile_info);
 
 }  // namespace art
 
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index dba5da0..7ffa38b 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -104,7 +104,9 @@
 }
 
 size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) {
-  return dex_file_->PwriteFully(buffer, length, offset) ? length : 0;
+  DCHECK_LE(offset + length, mem_map_->Size());
+  memcpy(mem_map_->Begin() + offset, buffer, length);
+  return length;
 }
 
 size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) {
@@ -236,12 +238,13 @@
 
 void DexWriter::WriteStrings() {
   uint32_t string_data_off[1];
-  for (std::unique_ptr<dex_ir::StringId>& string_id : header_.GetCollections().StringIds()) {
+  for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) {
     string_data_off[0] = string_id->DataItem()->GetOffset();
     Write(string_data_off, string_id->GetSize(), string_id->GetOffset());
   }
 
-  for (std::unique_ptr<dex_ir::StringData>& string_data : header_.GetCollections().StringDatas()) {
+  for (auto& string_data_pair : header_->GetCollections().StringDatas()) {
+    std::unique_ptr<dex_ir::StringData>& string_data = string_data_pair.second;
     uint32_t offset = string_data->GetOffset();
     offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset);
     Write(string_data->Data(), strlen(string_data->Data()), offset);
@@ -250,7 +253,7 @@
 
 void DexWriter::WriteTypes() {
   uint32_t descriptor_idx[1];
-  for (std::unique_ptr<dex_ir::TypeId>& type_id : header_.GetCollections().TypeIds()) {
+  for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) {
     descriptor_idx[0] = type_id->GetStringId()->GetIndex();
     Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset());
   }
@@ -259,7 +262,8 @@
 void DexWriter::WriteTypeLists() {
   uint32_t size[1];
   uint16_t list[1];
-  for (std::unique_ptr<dex_ir::TypeList>& type_list : header_.GetCollections().TypeLists()) {
+  for (auto& type_list_pair : header_->GetCollections().TypeLists()) {
+    std::unique_ptr<dex_ir::TypeList>& type_list = type_list_pair.second;
     size[0] = type_list->GetTypeList()->size();
     uint32_t offset = type_list->GetOffset();
     offset += Write(size, sizeof(uint32_t), offset);
@@ -272,7 +276,7 @@
 
 void DexWriter::WriteProtos() {
   uint32_t buffer[3];
-  for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_.GetCollections().ProtoIds()) {
+  for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) {
     buffer[0] = proto_id->Shorty()->GetIndex();
     buffer[1] = proto_id->ReturnType()->GetIndex();
     buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset();
@@ -282,7 +286,7 @@
 
 void DexWriter::WriteFields() {
   uint16_t buffer[4];
-  for (std::unique_ptr<dex_ir::FieldId>& field_id : header_.GetCollections().FieldIds()) {
+  for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) {
     buffer[0] = field_id->Class()->GetIndex();
     buffer[1] = field_id->Type()->GetIndex();
     buffer[2] = field_id->Name()->GetIndex();
@@ -293,7 +297,7 @@
 
 void DexWriter::WriteMethods() {
   uint16_t buffer[4];
-  for (std::unique_ptr<dex_ir::MethodId>& method_id : header_.GetCollections().MethodIds()) {
+  for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) {
     buffer[0] = method_id->Class()->GetIndex();
     buffer[1] = method_id->Proto()->GetIndex();
     buffer[2] = method_id->Name()->GetIndex();
@@ -303,16 +307,16 @@
 }
 
 void DexWriter::WriteEncodedArrays() {
-  for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array :
-      header_.GetCollections().EncodedArrayItems()) {
+  for (auto& encoded_array_pair : header_->GetCollections().EncodedArrayItems()) {
+    std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array = encoded_array_pair.second;
     WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset());
   }
 }
 
 void DexWriter::WriteAnnotations() {
   uint8_t visibility[1];
-  for (std::unique_ptr<dex_ir::AnnotationItem>& annotation :
-      header_.GetCollections().AnnotationItems()) {
+  for (auto& annotation_pair : header_->GetCollections().AnnotationItems()) {
+    std::unique_ptr<dex_ir::AnnotationItem>& annotation = annotation_pair.second;
     visibility[0] = annotation->GetVisibility();
     size_t offset = annotation->GetOffset();
     offset += Write(visibility, sizeof(uint8_t), offset);
@@ -323,8 +327,8 @@
 void DexWriter::WriteAnnotationSets() {
   uint32_t size[1];
   uint32_t annotation_off[1];
-  for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set :
-      header_.GetCollections().AnnotationSetItems()) {
+  for (auto& annotation_set_pair : header_->GetCollections().AnnotationSetItems()) {
+    std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set = annotation_set_pair.second;
     size[0] = annotation_set->GetItems()->size();
     size_t offset = annotation_set->GetOffset();
     offset += Write(size, sizeof(uint32_t), offset);
@@ -338,8 +342,8 @@
 void DexWriter::WriteAnnotationSetRefs() {
   uint32_t size[1];
   uint32_t annotations_off[1];
-  for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref :
-        header_.GetCollections().AnnotationSetRefLists()) {
+  for (auto& anno_set_ref_pair : header_->GetCollections().AnnotationSetRefLists()) {
+    std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref = anno_set_ref_pair.second;
     size[0] = annotation_set_ref->GetItems()->size();
     size_t offset = annotation_set_ref->GetOffset();
     offset += Write(size, sizeof(uint32_t), offset);
@@ -353,8 +357,9 @@
 void DexWriter::WriteAnnotationsDirectories() {
   uint32_t directory_buffer[4];
   uint32_t annotation_buffer[2];
-  for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory :
-          header_.GetCollections().AnnotationsDirectoryItems()) {
+  for (auto& annotations_directory_pair : header_->GetCollections().AnnotationsDirectoryItems()) {
+    std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory =
+        annotations_directory_pair.second;
     directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 :
         annotations_directory->GetClassAnnotation()->GetOffset();
     directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 :
@@ -393,15 +398,17 @@
 }
 
 void DexWriter::WriteDebugInfoItems() {
-  for (std::unique_ptr<dex_ir::DebugInfoItem>& info : header_.GetCollections().DebugInfoItems()) {
-    Write(info->GetDebugInfo(), info->GetDebugInfoSize(), info->GetOffset());
+  for (auto& debug_info_pair : header_->GetCollections().DebugInfoItems()) {
+    std::unique_ptr<dex_ir::DebugInfoItem>& debug_info = debug_info_pair.second;
+    Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), debug_info->GetOffset());
   }
 }
 
 void DexWriter::WriteCodeItems() {
   uint16_t uint16_buffer[4];
   uint32_t uint32_buffer[2];
-  for (std::unique_ptr<dex_ir::CodeItem>& code_item : header_.GetCollections().CodeItems()) {
+  for (auto& code_item_pair : header_->GetCollections().CodeItems()) {
+    std::unique_ptr<dex_ir::CodeItem>& code_item = code_item_pair.second;
     uint16_buffer[0] = code_item->RegistersSize();
     uint16_buffer[1] = code_item->InsSize();
     uint16_buffer[2] = code_item->OutsSize();
@@ -446,7 +453,7 @@
 
 void DexWriter::WriteClasses() {
   uint32_t class_def_buffer[8];
-  for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_.GetCollections().ClassDefs()) {
+  for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
     class_def_buffer[0] = class_def->ClassType()->GetIndex();
     class_def_buffer[1] = class_def->GetAccessFlags();
     class_def_buffer[2] = class_def->Superclass() == nullptr ? DexFile::kDexNoIndex :
@@ -464,7 +471,8 @@
     Write(class_def_buffer, class_def->GetSize(), offset);
   }
 
-  for (std::unique_ptr<dex_ir::ClassData>& class_data : header_.GetCollections().ClassDatas()) {
+  for (auto& class_data_pair : header_->GetCollections().ClassDatas()) {
+    std::unique_ptr<dex_ir::ClassData>& class_data = class_data_pair.second;
     size_t offset = class_data->GetOffset();
     offset += WriteUleb128(class_data->StaticFields()->size(), offset);
     offset += WriteUleb128(class_data->InstanceFields()->size(), offset);
@@ -491,7 +499,7 @@
 };
 
 void DexWriter::WriteMapItem() {
-  dex_ir::Collections& collection = header_.GetCollections();
+  dex_ir::Collections& collection = header_->GetCollections();
   std::priority_queue<MapItemContainer> queue;
 
   // Header and index section.
@@ -522,7 +530,7 @@
   }
 
   // Data section.
-  queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapItemOffset()));
+  queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapListOffset()));
   if (collection.TypeListsSize() != 0) {
     queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(),
         collection.TypeListsOffset()));
@@ -564,7 +572,7 @@
         collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset()));
   }
 
-  uint32_t offset = collection.MapItemOffset();
+  uint32_t offset = collection.MapListOffset();
   uint16_t uint16_buffer[2];
   uint32_t uint32_buffer[2];
   uint16_buffer[1] = 0;
@@ -583,19 +591,19 @@
 
 void DexWriter::WriteHeader() {
   uint32_t buffer[20];
-  dex_ir::Collections& collections = header_.GetCollections();
+  dex_ir::Collections& collections = header_->GetCollections();
   size_t offset = 0;
-  offset += Write(header_.Magic(), 8 * sizeof(uint8_t), offset);
-  buffer[0] = header_.Checksum();
+  offset += Write(header_->Magic(), 8 * sizeof(uint8_t), offset);
+  buffer[0] = header_->Checksum();
   offset += Write(buffer, sizeof(uint32_t), offset);
-  offset += Write(header_.Signature(), 20 * sizeof(uint8_t), offset);
-  uint32_t file_size = header_.FileSize();
+  offset += Write(header_->Signature(), 20 * sizeof(uint8_t), offset);
+  uint32_t file_size = header_->FileSize();
   buffer[0] = file_size;
-  buffer[1] = header_.GetSize();
-  buffer[2] = header_.EndianTag();
-  buffer[3] = header_.LinkSize();
-  buffer[4] = header_.LinkOffset();
-  buffer[5] = collections.MapItemOffset();
+  buffer[1] = header_->GetSize();
+  buffer[2] = header_->EndianTag();
+  buffer[3] = header_->LinkSize();
+  buffer[4] = header_->LinkOffset();
+  buffer[5] = collections.MapListOffset();
   buffer[6] = collections.StringIdsSize();
   buffer[7] = collections.StringIdsOffset();
   buffer[8] = collections.TypeIdsSize();
@@ -617,12 +625,7 @@
   Write(buffer, 20 * sizeof(uint32_t), offset);
 }
 
-void DexWriter::WriteFile() {
-  if (dex_file_.get() == nullptr) {
-    fprintf(stderr, "Can't open output dex file\n");
-    return;
-  }
-
+void DexWriter::WriteMemMap() {
   WriteStrings();
   WriteTypes();
   WriteTypeLists();
@@ -641,8 +644,9 @@
   WriteHeader();
 }
 
-void DexWriter::OutputDexFile(dex_ir::Header& header, const char* file_name) {
-  (new DexWriter(header, file_name))->WriteFile();
+void DexWriter::Output(dex_ir::Header* header, MemMap* mem_map) {
+  DexWriter dex_writer(header, mem_map);
+  dex_writer.WriteMemMap();
 }
 
 }  // namespace art
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index 9104295..fb76e5c 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -21,19 +21,19 @@
 
 #include "base/unix_file/fd_file.h"
 #include "dex_ir.h"
+#include "mem_map.h"
 #include "os.h"
 
 namespace art {
 
 class DexWriter {
  public:
-  DexWriter(dex_ir::Header& header, const char* file_name) : header_(header),
-      dex_file_(OS::CreateEmptyFileWriteOnly(file_name)) { }
+  DexWriter(dex_ir::Header* header, MemMap* mem_map) : header_(header), mem_map_(mem_map) { }
 
-  static void OutputDexFile(dex_ir::Header& header, const char* file_name);
+  static void Output(dex_ir::Header* header, MemMap* mem_map);
 
  private:
-  void WriteFile();
+  void WriteMemMap();
 
   size_t Write(const void* buffer, size_t length, size_t offset);
   size_t WriteSleb128(uint32_t value, size_t offset);
@@ -62,13 +62,12 @@
   void WriteMapItem();
   void WriteHeader();
 
-  dex_ir::Header& header_;
-  std::unique_ptr<File> dex_file_;
+  dex_ir::Header* const header_;
+  MemMap* const mem_map_;
 
   DISALLOW_COPY_AND_ASSIGN(DexWriter);
 };
 
-
 }  // namespace art
 
 #endif  // ART_DEXLAYOUT_DEX_WRITER_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index aa80655..cfe4837 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -37,27 +37,13 @@
 #include "dex_visualize.h"
 #include "dex_writer.h"
 #include "jit/offline_profiling_info.h"
+#include "mem_map.h"
 #include "os.h"
 #include "utils.h"
 
 namespace art {
 
 /*
- * Options parsed in main driver.
- */
-struct Options options_;
-
-/*
- * Output file. Defaults to stdout.
- */
-FILE* out_file_ = stdout;
-
-/*
- * Profile information file.
- */
-ProfileCompilationInfo* profile_info_ = nullptr;
-
-/*
  * Flags for use with createAccessFlagStr().
  */
 enum AccessFor {
@@ -301,420 +287,65 @@
 /*
  * Dumps a string value with some escape characters.
  */
-static void DumpEscapedString(const char* p) {
-  fputs("\"", out_file_);
+static void DumpEscapedString(const char* p, FILE* out_file) {
+  fputs("\"", out_file);
   for (; *p; p++) {
     switch (*p) {
       case '\\':
-        fputs("\\\\", out_file_);
+        fputs("\\\\", out_file);
         break;
       case '\"':
-        fputs("\\\"", out_file_);
+        fputs("\\\"", out_file);
         break;
       case '\t':
-        fputs("\\t", out_file_);
+        fputs("\\t", out_file);
         break;
       case '\n':
-        fputs("\\n", out_file_);
+        fputs("\\n", out_file);
         break;
       case '\r':
-        fputs("\\r", out_file_);
+        fputs("\\r", out_file);
         break;
       default:
-        putc(*p, out_file_);
+        putc(*p, out_file);
     }  // switch
   }  // for
-  fputs("\"", out_file_);
+  fputs("\"", out_file);
 }
 
 /*
  * Dumps a string as an XML attribute value.
  */
-static void DumpXmlAttribute(const char* p) {
+static void DumpXmlAttribute(const char* p, FILE* out_file) {
   for (; *p; p++) {
     switch (*p) {
       case '&':
-        fputs("&amp;", out_file_);
+        fputs("&amp;", out_file);
         break;
       case '<':
-        fputs("&lt;", out_file_);
+        fputs("&lt;", out_file);
         break;
       case '>':
-        fputs("&gt;", out_file_);
+        fputs("&gt;", out_file);
         break;
       case '"':
-        fputs("&quot;", out_file_);
+        fputs("&quot;", out_file);
         break;
       case '\t':
-        fputs("&#x9;", out_file_);
+        fputs("&#x9;", out_file);
         break;
       case '\n':
-        fputs("&#xA;", out_file_);
+        fputs("&#xA;", out_file);
         break;
       case '\r':
-        fputs("&#xD;", out_file_);
+        fputs("&#xD;", out_file);
         break;
       default:
-        putc(*p, out_file_);
+        putc(*p, out_file);
     }  // switch
   }  // 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::EncodedValue* data) {
-  switch (data->Type()) {
-    case DexFile::kDexAnnotationByte:
-      fprintf(out_file_, "%" PRId8, data->GetByte());
-      break;
-    case DexFile::kDexAnnotationShort:
-      fprintf(out_file_, "%" PRId16, data->GetShort());
-      break;
-    case DexFile::kDexAnnotationChar:
-      fprintf(out_file_, "%" PRIu16, data->GetChar());
-      break;
-    case DexFile::kDexAnnotationInt:
-      fprintf(out_file_, "%" PRId32, data->GetInt());
-      break;
-    case DexFile::kDexAnnotationLong:
-      fprintf(out_file_, "%" PRId64, data->GetLong());
-      break;
-    case DexFile::kDexAnnotationFloat: {
-      fprintf(out_file_, "%g", data->GetFloat());
-      break;
-    }
-    case DexFile::kDexAnnotationDouble: {
-      fprintf(out_file_, "%g", data->GetDouble());
-      break;
-    }
-    case DexFile::kDexAnnotationString: {
-      dex_ir::StringId* string_id = data->GetStringId();
-      if (options_.output_format_ == kOutputPlain) {
-        DumpEscapedString(string_id->Data());
-      } else {
-        DumpXmlAttribute(string_id->Data());
-      }
-      break;
-    }
-    case DexFile::kDexAnnotationType: {
-      dex_ir::TypeId* type_id = data->GetTypeId();
-      fputs(type_id->GetStringId()->Data(), out_file_);
-      break;
-    }
-    case DexFile::kDexAnnotationField:
-    case DexFile::kDexAnnotationEnum: {
-      dex_ir::FieldId* field_id = data->GetFieldId();
-      fputs(field_id->Name()->Data(), out_file_);
-      break;
-    }
-    case DexFile::kDexAnnotationMethod: {
-      dex_ir::MethodId* method_id = data->GetMethodId();
-      fputs(method_id->Name()->Data(), out_file_);
-      break;
-    }
-    case DexFile::kDexAnnotationArray: {
-      fputc('{', out_file_);
-      // Display all elements.
-      for (auto& value : *data->GetEncodedArray()->GetEncodedValues()) {
-        fputc(' ', out_file_);
-        DumpEncodedValue(value.get());
-      }
-      fputs(" }", out_file_);
-      break;
-    }
-    case DexFile::kDexAnnotationAnnotation: {
-      DumpEncodedAnnotation(data->GetEncodedAnnotation());
-      break;
-    }
-    case DexFile::kDexAnnotationNull:
-      fputs("null", out_file_);
-      break;
-    case DexFile::kDexAnnotationBoolean:
-      fputs(StrBool(data->GetBoolean()), out_file_);
-      break;
-    default:
-      fputs("????", out_file_);
-      break;
-  }  // switch
-}
-
-/*
- * Dumps the file 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);
-  fprintf(out_file_, "checksum            : %08x\n", header->Checksum());
-  fprintf(out_file_, "signature           : %02x%02x...%02x%02x\n",
-          header->Signature()[0], header->Signature()[1],
-          header->Signature()[DexFile::kSha1DigestSize - 2],
-          header->Signature()[DexFile::kSha1DigestSize - 1]);
-  fprintf(out_file_, "file_size           : %d\n", header->FileSize());
-  fprintf(out_file_, "header_size         : %d\n", header->HeaderSize());
-  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", collections.StringIdsSize());
-  fprintf(out_file_, "string_ids_off      : %d (0x%06x)\n",
-          collections.StringIdsOffset(), collections.StringIdsOffset());
-  fprintf(out_file_, "type_ids_size       : %d\n", collections.TypeIdsSize());
-  fprintf(out_file_, "type_ids_off        : %d (0x%06x)\n",
-          collections.TypeIdsOffset(), collections.TypeIdsOffset());
-  fprintf(out_file_, "proto_ids_size      : %d\n", collections.ProtoIdsSize());
-  fprintf(out_file_, "proto_ids_off       : %d (0x%06x)\n",
-          collections.ProtoIdsOffset(), collections.ProtoIdsOffset());
-  fprintf(out_file_, "field_ids_size      : %d\n", collections.FieldIdsSize());
-  fprintf(out_file_, "field_ids_off       : %d (0x%06x)\n",
-          collections.FieldIdsOffset(), collections.FieldIdsOffset());
-  fprintf(out_file_, "method_ids_size     : %d\n", collections.MethodIdsSize());
-  fprintf(out_file_, "method_ids_off      : %d (0x%06x)\n",
-          collections.MethodIdsOffset(), collections.MethodIdsOffset());
-  fprintf(out_file_, "class_defs_size     : %d\n", collections.ClassDefsSize());
-  fprintf(out_file_, "class_defs_off      : %d (0x%06x)\n",
-          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());
-}
-
-/*
- * Dumps a class_def_item.
- */
-static void DumpClassDef(dex_ir::Header* header, int idx) {
-  // General class information.
-  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()->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()->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()->GetIndex();
-  }
-  fprintf(out_file_, "source_file_idx     : %d\n", source_file_offset);
-  uint32_t annotations_offset = 0;
-  if (class_def->Annotations() != nullptr) {
-    annotations_offset = class_def->Annotations()->GetOffset();
-  }
-  fprintf(out_file_, "annotations_off     : %d (0x%06x)\n",
-          annotations_offset, annotations_offset);
-  if (class_def->GetClassData() == nullptr) {
-    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n", 0, 0);
-  } else {
-    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n",
-            class_def->GetClassData()->GetOffset(), class_def->GetClassData()->GetOffset());
-  }
-
-  // Fields and methods.
-  dex_ir::ClassData* class_data = class_def->GetClassData();
-  if (class_data != nullptr && class_data->StaticFields() != nullptr) {
-    fprintf(out_file_, "static_fields_size  : %zu\n", class_data->StaticFields()->size());
-  } else {
-    fprintf(out_file_, "static_fields_size  : 0\n");
-  }
-  if (class_data != nullptr && class_data->InstanceFields() != nullptr) {
-    fprintf(out_file_, "instance_fields_size: %zu\n", class_data->InstanceFields()->size());
-  } else {
-    fprintf(out_file_, "instance_fields_size: 0\n");
-  }
-  if (class_data != nullptr && class_data->DirectMethods() != nullptr) {
-    fprintf(out_file_, "direct_methods_size : %zu\n", class_data->DirectMethods()->size());
-  } else {
-    fprintf(out_file_, "direct_methods_size : 0\n");
-  }
-  if (class_data != nullptr && class_data->VirtualMethods() != nullptr) {
-    fprintf(out_file_, "virtual_methods_size: %zu\n", class_data->VirtualMethods()->size());
-  } else {
-    fprintf(out_file_, "virtual_methods_size: 0\n");
-  }
-  fprintf(out_file_, "\n");
-}
-
-/**
- * Dumps an annotation set item.
- */
-static void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) {
-  if (set_item == nullptr || set_item->GetItems()->size() == 0) {
-    fputs("  empty-annotation-set\n", out_file_);
-    return;
-  }
-  for (dex_ir::AnnotationItem* annotation : *set_item->GetItems()) {
-    if (annotation == nullptr) {
-      continue;
-    }
-    fputs("  ", out_file_);
-    switch (annotation->GetVisibility()) {
-      case DexFile::kDexVisibilityBuild:   fputs("VISIBILITY_BUILD ",   out_file_); break;
-      case DexFile::kDexVisibilityRuntime: fputs("VISIBILITY_RUNTIME ", out_file_); break;
-      case DexFile::kDexVisibilitySystem:  fputs("VISIBILITY_SYSTEM ",  out_file_); break;
-      default:                             fputs("VISIBILITY_UNKNOWN ", out_file_); break;
-    }  // switch
-    DumpEncodedAnnotation(annotation->GetAnnotation());
-    fputc('\n', out_file_);
-  }
-}
-
-/*
- * Dumps class annotations.
- */
-static void DumpClassAnnotations(dex_ir::Header* header, int idx) {
-  dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
-  dex_ir::AnnotationsDirectoryItem* annotations_directory = class_def->Annotations();
-  if (annotations_directory == nullptr) {
-    return;  // none
-  }
-
-  fprintf(out_file_, "Class #%d annotations:\n", idx);
-
-  dex_ir::AnnotationSetItem* class_set_item = annotations_directory->GetClassAnnotation();
-  dex_ir::FieldAnnotationVector* fields = annotations_directory->GetFieldAnnotations();
-  dex_ir::MethodAnnotationVector* methods = annotations_directory->GetMethodAnnotations();
-  dex_ir::ParameterAnnotationVector* parameters = annotations_directory->GetParameterAnnotations();
-
-  // Annotations on the class itself.
-  if (class_set_item != nullptr) {
-    fprintf(out_file_, "Annotations on class\n");
-    DumpAnnotationSetItem(class_set_item);
-  }
-
-  // Annotations on fields.
-  if (fields != nullptr) {
-    for (auto& field : *fields) {
-      const dex_ir::FieldId* field_id = field->GetFieldId();
-      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());
-    }
-  }
-
-  // Annotations on methods.
-  if (methods != nullptr) {
-    for (auto& method : *methods) {
-      const dex_ir::MethodId* method_id = method->GetMethodId();
-      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());
-    }
-  }
-
-  // Annotations on method parameters.
-  if (parameters != nullptr) {
-    for (auto& parameter : *parameters) {
-      const dex_ir::MethodId* method_id = parameter->GetMethodId();
-      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 (dex_ir::AnnotationSetItem* annotation : *parameter->GetAnnotations()->GetItems()) {
-        fprintf(out_file_, "#%u\n", j);
-        DumpAnnotationSetItem(annotation);
-        ++j;
-      }
-    }
-  }
-
-  fputc('\n', out_file_);
-}
-
-/*
- * Dumps an interface that a class declares to implement.
- */
-static void DumpInterface(const dex_ir::TypeId* type_item, int i) {
-  const char* interface_name = type_item->GetStringId()->Data();
-  if (options_.output_format_ == kOutputPlain) {
-    fprintf(out_file_, "    #%d              : '%s'\n", i, interface_name);
-  } else {
-    std::string dot(DescriptorToDotWrapper(interface_name));
-    fprintf(out_file_, "<implements name=\"%s\">\n</implements>\n", dot.c_str());
-  }
-}
-
-/*
- * Dumps the catches table associated with the code.
- */
-static void DumpCatches(const dex_ir::CodeItem* code) {
-  const uint16_t tries_size = code->TriesSize();
-
-  // No catch table.
-  if (tries_size == 0) {
-    fprintf(out_file_, "      catches       : (none)\n");
-    return;
-  }
-
-  // Dump all table entries.
-  fprintf(out_file_, "      catches       : %d\n", tries_size);
-  std::vector<std::unique_ptr<const dex_ir::TryItem>>* tries = code->Tries();
-  for (uint32_t i = 0; i < tries_size; i++) {
-    const dex_ir::TryItem* try_item = (*tries)[i].get();
-    const uint32_t start = try_item->StartAddr();
-    const uint32_t end = start + try_item->InsnCount();
-    fprintf(out_file_, "        0x%04x - 0x%04x\n", start, end);
-    for (auto& handler : *try_item->GetHandlers()->GetHandlers()) {
-      const dex_ir::TypeId* type_id = handler->GetTypeId();
-      const char* descriptor = (type_id == nullptr) ? "<any>" : type_id->GetStringId()->Data();
-      fprintf(out_file_, "          %s -> 0x%04x\n", descriptor, handler->GetAddress());
-    }  // for
-  }  // for
-}
-
-/*
- * Dumps all positions table entries associated with the code.
- */
-static void DumpPositionInfo(const dex_ir::CodeItem* code) {
-  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
-  if (debug_info == nullptr) {
-    return;
-  }
-  std::vector<std::unique_ptr<dex_ir::PositionInfo>>& positions = debug_info->GetPositionInfo();
-  for (size_t i = 0; i < positions.size(); ++i) {
-    fprintf(out_file_, "        0x%04x line=%d\n", positions[i]->address_, positions[i]->line_);
-  }
-}
-
-/*
- * Dumps all locals table entries associated with the code.
- */
-static void DumpLocalInfo(const dex_ir::CodeItem* code) {
-  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
-  if (debug_info == nullptr) {
-    return;
-  }
-  std::vector<std::unique_ptr<dex_ir::LocalInfo>>& locals = debug_info->GetLocalInfo();
-  for (size_t i = 0; i < locals.size(); ++i) {
-    dex_ir::LocalInfo* entry = locals[i].get();
-    fprintf(out_file_, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
-            entry->start_address_, entry->end_address_, entry->reg_,
-            entry->name_.c_str(), entry->descriptor_.c_str(), entry->signature_.c_str());
-  }
-}
-
 /*
  * Helper for dumpInstruction(), which builds the string
  * representation for the index in the given instruction.
@@ -723,11 +354,10 @@
 static std::unique_ptr<char[]> IndexString(dex_ir::Header* header,
                                            const Instruction* dec_insn,
                                            size_t buf_size) {
-  static const uint32_t kInvalidIndex = std::numeric_limits<uint32_t>::max();
   std::unique_ptr<char[]> buf(new char[buf_size]);
   // Determine index and width of the string.
   uint32_t index = 0;
-  uint32_t secondary_index = kInvalidIndex;
+  uint32_t secondary_index = DexFile::kDexNoIndex;
   uint32_t width = 4;
   switch (Instruction::FormatOf(dec_insn->Opcode())) {
     // SOME NOT SUPPORTED:
@@ -756,7 +386,6 @@
       index = dec_insn->VRegB();
       secondary_index = dec_insn->VRegH();
       width = 4;
-      break;
     default:
       break;
   }  // switch
@@ -821,9 +450,6 @@
     case Instruction::kIndexFieldOffset:
       outSize = snprintf(buf.get(), buf_size, "[obj+%0*x]", width, index);
       break;
-    // SOME NOT SUPPORTED:
-    // case Instruction::kIndexVaries:
-    // case Instruction::kIndexInlineMethod:
     case Instruction::kIndexMethodAndProtoRef: {
       std::string method("<method?>");
       std::string proto("<proto?>");
@@ -840,8 +466,11 @@
       }
       outSize = snprintf(buf.get(), buf_size, "%s, %s // method@%0*x, proto@%0*x",
                          method.c_str(), proto.c_str(), width, index, width, secondary_index);
-      }
-      break;
+    }
+    break;
+    // SOME NOT SUPPORTED:
+    // case Instruction::kIndexVaries:
+    // case Instruction::kIndexInlineMethod:
     default:
       outSize = snprintf(buf.get(), buf_size, "<?>");
       break;
@@ -858,11 +487,365 @@
 }
 
 /*
+ * Dumps encoded annotation.
+ */
+void DexLayout::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.
+ */
+void DexLayout::DumpEncodedValue(const dex_ir::EncodedValue* data) {
+  switch (data->Type()) {
+    case DexFile::kDexAnnotationByte:
+      fprintf(out_file_, "%" PRId8, data->GetByte());
+      break;
+    case DexFile::kDexAnnotationShort:
+      fprintf(out_file_, "%" PRId16, data->GetShort());
+      break;
+    case DexFile::kDexAnnotationChar:
+      fprintf(out_file_, "%" PRIu16, data->GetChar());
+      break;
+    case DexFile::kDexAnnotationInt:
+      fprintf(out_file_, "%" PRId32, data->GetInt());
+      break;
+    case DexFile::kDexAnnotationLong:
+      fprintf(out_file_, "%" PRId64, data->GetLong());
+      break;
+    case DexFile::kDexAnnotationFloat: {
+      fprintf(out_file_, "%g", data->GetFloat());
+      break;
+    }
+    case DexFile::kDexAnnotationDouble: {
+      fprintf(out_file_, "%g", data->GetDouble());
+      break;
+    }
+    case DexFile::kDexAnnotationString: {
+      dex_ir::StringId* string_id = data->GetStringId();
+      if (options_.output_format_ == kOutputPlain) {
+        DumpEscapedString(string_id->Data(), out_file_);
+      } else {
+        DumpXmlAttribute(string_id->Data(), out_file_);
+      }
+      break;
+    }
+    case DexFile::kDexAnnotationType: {
+      dex_ir::TypeId* type_id = data->GetTypeId();
+      fputs(type_id->GetStringId()->Data(), out_file_);
+      break;
+    }
+    case DexFile::kDexAnnotationField:
+    case DexFile::kDexAnnotationEnum: {
+      dex_ir::FieldId* field_id = data->GetFieldId();
+      fputs(field_id->Name()->Data(), out_file_);
+      break;
+    }
+    case DexFile::kDexAnnotationMethod: {
+      dex_ir::MethodId* method_id = data->GetMethodId();
+      fputs(method_id->Name()->Data(), out_file_);
+      break;
+    }
+    case DexFile::kDexAnnotationArray: {
+      fputc('{', out_file_);
+      // Display all elements.
+      for (auto& value : *data->GetEncodedArray()->GetEncodedValues()) {
+        fputc(' ', out_file_);
+        DumpEncodedValue(value.get());
+      }
+      fputs(" }", out_file_);
+      break;
+    }
+    case DexFile::kDexAnnotationAnnotation: {
+      DumpEncodedAnnotation(data->GetEncodedAnnotation());
+      break;
+    }
+    case DexFile::kDexAnnotationNull:
+      fputs("null", out_file_);
+      break;
+    case DexFile::kDexAnnotationBoolean:
+      fputs(StrBool(data->GetBoolean()), out_file_);
+      break;
+    default:
+      fputs("????", out_file_);
+      break;
+  }  // switch
+}
+
+/*
+ * Dumps the file header.
+ */
+void DexLayout::DumpFileHeader() {
+  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);
+  fprintf(out_file_, "checksum            : %08x\n", header_->Checksum());
+  fprintf(out_file_, "signature           : %02x%02x...%02x%02x\n",
+          header_->Signature()[0], header_->Signature()[1],
+          header_->Signature()[DexFile::kSha1DigestSize - 2],
+          header_->Signature()[DexFile::kSha1DigestSize - 1]);
+  fprintf(out_file_, "file_size           : %d\n", header_->FileSize());
+  fprintf(out_file_, "header_size         : %d\n", header_->HeaderSize());
+  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", collections.StringIdsSize());
+  fprintf(out_file_, "string_ids_off      : %d (0x%06x)\n",
+          collections.StringIdsOffset(), collections.StringIdsOffset());
+  fprintf(out_file_, "type_ids_size       : %d\n", collections.TypeIdsSize());
+  fprintf(out_file_, "type_ids_off        : %d (0x%06x)\n",
+          collections.TypeIdsOffset(), collections.TypeIdsOffset());
+  fprintf(out_file_, "proto_ids_size      : %d\n", collections.ProtoIdsSize());
+  fprintf(out_file_, "proto_ids_off       : %d (0x%06x)\n",
+          collections.ProtoIdsOffset(), collections.ProtoIdsOffset());
+  fprintf(out_file_, "field_ids_size      : %d\n", collections.FieldIdsSize());
+  fprintf(out_file_, "field_ids_off       : %d (0x%06x)\n",
+          collections.FieldIdsOffset(), collections.FieldIdsOffset());
+  fprintf(out_file_, "method_ids_size     : %d\n", collections.MethodIdsSize());
+  fprintf(out_file_, "method_ids_off      : %d (0x%06x)\n",
+          collections.MethodIdsOffset(), collections.MethodIdsOffset());
+  fprintf(out_file_, "class_defs_size     : %d\n", collections.ClassDefsSize());
+  fprintf(out_file_, "class_defs_off      : %d (0x%06x)\n",
+          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());
+}
+
+/*
+ * Dumps a class_def_item.
+ */
+void DexLayout::DumpClassDef(int idx) {
+  // General class information.
+  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()->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()->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()->GetIndex();
+  }
+  fprintf(out_file_, "source_file_idx     : %d\n", source_file_offset);
+  uint32_t annotations_offset = 0;
+  if (class_def->Annotations() != nullptr) {
+    annotations_offset = class_def->Annotations()->GetOffset();
+  }
+  fprintf(out_file_, "annotations_off     : %d (0x%06x)\n",
+          annotations_offset, annotations_offset);
+  if (class_def->GetClassData() == nullptr) {
+    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n", 0, 0);
+  } else {
+    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n",
+            class_def->GetClassData()->GetOffset(), class_def->GetClassData()->GetOffset());
+  }
+
+  // Fields and methods.
+  dex_ir::ClassData* class_data = class_def->GetClassData();
+  if (class_data != nullptr && class_data->StaticFields() != nullptr) {
+    fprintf(out_file_, "static_fields_size  : %zu\n", class_data->StaticFields()->size());
+  } else {
+    fprintf(out_file_, "static_fields_size  : 0\n");
+  }
+  if (class_data != nullptr && class_data->InstanceFields() != nullptr) {
+    fprintf(out_file_, "instance_fields_size: %zu\n", class_data->InstanceFields()->size());
+  } else {
+    fprintf(out_file_, "instance_fields_size: 0\n");
+  }
+  if (class_data != nullptr && class_data->DirectMethods() != nullptr) {
+    fprintf(out_file_, "direct_methods_size : %zu\n", class_data->DirectMethods()->size());
+  } else {
+    fprintf(out_file_, "direct_methods_size : 0\n");
+  }
+  if (class_data != nullptr && class_data->VirtualMethods() != nullptr) {
+    fprintf(out_file_, "virtual_methods_size: %zu\n", class_data->VirtualMethods()->size());
+  } else {
+    fprintf(out_file_, "virtual_methods_size: 0\n");
+  }
+  fprintf(out_file_, "\n");
+}
+
+/**
+ * Dumps an annotation set item.
+ */
+void DexLayout::DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) {
+  if (set_item == nullptr || set_item->GetItems()->size() == 0) {
+    fputs("  empty-annotation-set\n", out_file_);
+    return;
+  }
+  for (dex_ir::AnnotationItem* annotation : *set_item->GetItems()) {
+    if (annotation == nullptr) {
+      continue;
+    }
+    fputs("  ", out_file_);
+    switch (annotation->GetVisibility()) {
+      case DexFile::kDexVisibilityBuild:   fputs("VISIBILITY_BUILD ",   out_file_); break;
+      case DexFile::kDexVisibilityRuntime: fputs("VISIBILITY_RUNTIME ", out_file_); break;
+      case DexFile::kDexVisibilitySystem:  fputs("VISIBILITY_SYSTEM ",  out_file_); break;
+      default:                             fputs("VISIBILITY_UNKNOWN ", out_file_); break;
+    }  // switch
+    DumpEncodedAnnotation(annotation->GetAnnotation());
+    fputc('\n', out_file_);
+  }
+}
+
+/*
+ * Dumps class annotations.
+ */
+void DexLayout::DumpClassAnnotations(int idx) {
+  dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx);
+  dex_ir::AnnotationsDirectoryItem* annotations_directory = class_def->Annotations();
+  if (annotations_directory == nullptr) {
+    return;  // none
+  }
+
+  fprintf(out_file_, "Class #%d annotations:\n", idx);
+
+  dex_ir::AnnotationSetItem* class_set_item = annotations_directory->GetClassAnnotation();
+  dex_ir::FieldAnnotationVector* fields = annotations_directory->GetFieldAnnotations();
+  dex_ir::MethodAnnotationVector* methods = annotations_directory->GetMethodAnnotations();
+  dex_ir::ParameterAnnotationVector* parameters = annotations_directory->GetParameterAnnotations();
+
+  // Annotations on the class itself.
+  if (class_set_item != nullptr) {
+    fprintf(out_file_, "Annotations on class\n");
+    DumpAnnotationSetItem(class_set_item);
+  }
+
+  // Annotations on fields.
+  if (fields != nullptr) {
+    for (auto& field : *fields) {
+      const dex_ir::FieldId* field_id = field->GetFieldId();
+      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());
+    }
+  }
+
+  // Annotations on methods.
+  if (methods != nullptr) {
+    for (auto& method : *methods) {
+      const dex_ir::MethodId* method_id = method->GetMethodId();
+      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());
+    }
+  }
+
+  // Annotations on method parameters.
+  if (parameters != nullptr) {
+    for (auto& parameter : *parameters) {
+      const dex_ir::MethodId* method_id = parameter->GetMethodId();
+      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 (dex_ir::AnnotationSetItem* annotation : *parameter->GetAnnotations()->GetItems()) {
+        fprintf(out_file_, "#%u\n", j);
+        DumpAnnotationSetItem(annotation);
+        ++j;
+      }
+    }
+  }
+
+  fputc('\n', out_file_);
+}
+
+/*
+ * Dumps an interface that a class declares to implement.
+ */
+void DexLayout::DumpInterface(const dex_ir::TypeId* type_item, int i) {
+  const char* interface_name = type_item->GetStringId()->Data();
+  if (options_.output_format_ == kOutputPlain) {
+    fprintf(out_file_, "    #%d              : '%s'\n", i, interface_name);
+  } else {
+    std::string dot(DescriptorToDotWrapper(interface_name));
+    fprintf(out_file_, "<implements name=\"%s\">\n</implements>\n", dot.c_str());
+  }
+}
+
+/*
+ * Dumps the catches table associated with the code.
+ */
+void DexLayout::DumpCatches(const dex_ir::CodeItem* code) {
+  const uint16_t tries_size = code->TriesSize();
+
+  // No catch table.
+  if (tries_size == 0) {
+    fprintf(out_file_, "      catches       : (none)\n");
+    return;
+  }
+
+  // Dump all table entries.
+  fprintf(out_file_, "      catches       : %d\n", tries_size);
+  std::vector<std::unique_ptr<const dex_ir::TryItem>>* tries = code->Tries();
+  for (uint32_t i = 0; i < tries_size; i++) {
+    const dex_ir::TryItem* try_item = (*tries)[i].get();
+    const uint32_t start = try_item->StartAddr();
+    const uint32_t end = start + try_item->InsnCount();
+    fprintf(out_file_, "        0x%04x - 0x%04x\n", start, end);
+    for (auto& handler : *try_item->GetHandlers()->GetHandlers()) {
+      const dex_ir::TypeId* type_id = handler->GetTypeId();
+      const char* descriptor = (type_id == nullptr) ? "<any>" : type_id->GetStringId()->Data();
+      fprintf(out_file_, "          %s -> 0x%04x\n", descriptor, handler->GetAddress());
+    }  // for
+  }  // for
+}
+
+/*
+ * Dumps all positions table entries associated with the code.
+ */
+void DexLayout::DumpPositionInfo(const dex_ir::CodeItem* code) {
+  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
+  if (debug_info == nullptr) {
+    return;
+  }
+  std::vector<std::unique_ptr<dex_ir::PositionInfo>>& positions = debug_info->GetPositionInfo();
+  for (size_t i = 0; i < positions.size(); ++i) {
+    fprintf(out_file_, "        0x%04x line=%d\n", positions[i]->address_, positions[i]->line_);
+  }
+}
+
+/*
+ * Dumps all locals table entries associated with the code.
+ */
+void DexLayout::DumpLocalInfo(const dex_ir::CodeItem* code) {
+  dex_ir::DebugInfoItem* debug_info = code->DebugInfo();
+  if (debug_info == nullptr) {
+    return;
+  }
+  std::vector<std::unique_ptr<dex_ir::LocalInfo>>& locals = debug_info->GetLocalInfo();
+  for (size_t i = 0; i < locals.size(); ++i) {
+    dex_ir::LocalInfo* entry = locals[i].get();
+    fprintf(out_file_, "        0x%04x - 0x%04x reg=%d %s %s %s\n",
+            entry->start_address_, entry->end_address_, entry->reg_,
+            entry->name_.c_str(), entry->descriptor_.c_str(), entry->signature_.c_str());
+  }
+}
+
+/*
  * Dumps a single instruction.
  */
-static void DumpInstruction(dex_ir::Header* header, const dex_ir::CodeItem* code,
-                            uint32_t code_offset, uint32_t insn_idx, uint32_t insn_width,
-                            const Instruction* dec_insn) {
+void DexLayout::DumpInstruction(const dex_ir::CodeItem* code,
+                                uint32_t code_offset,
+                                uint32_t insn_idx,
+                                uint32_t insn_width,
+                                const Instruction* dec_insn) {
   // Address of instruction (expressed as byte offset).
   fprintf(out_file_, "%06x:", code_offset + 0x10 + insn_idx * 2);
 
@@ -901,7 +884,7 @@
   // Set up additional argument.
   std::unique_ptr<char[]> index_buf;
   if (Instruction::IndexTypeOf(dec_insn->Opcode()) != Instruction::kIndexNone) {
-    index_buf = IndexString(header, dec_insn, 200);
+    index_buf = IndexString(header_, dec_insn, 200);
   }
 
   // Dump the instruction.
@@ -1073,9 +1056,8 @@
 /*
  * Dumps a bytecode disassembly.
  */
-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->GetCollections().GetMethodId(idx);
+void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) {
+  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();
@@ -1094,7 +1076,7 @@
       fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insn_idx);
       break;
     }
-    DumpInstruction(header, code, code_offset, insn_idx, insn_width, instruction);
+    DumpInstruction(code, code_offset, insn_idx, insn_width, instruction);
     insn_idx += insn_width;
   }  // for
 }
@@ -1102,8 +1084,7 @@
 /*
  * Dumps code of a method.
  */
-static void DumpCode(dex_ir::Header* header, uint32_t idx, const dex_ir::CodeItem* code,
-                     uint32_t code_offset) {
+void DexLayout::DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset) {
   fprintf(out_file_, "      registers     : %d\n", code->RegistersSize());
   fprintf(out_file_, "      ins           : %d\n", code->InsSize());
   fprintf(out_file_, "      outs          : %d\n", code->OutsSize());
@@ -1112,7 +1093,7 @@
 
   // Bytecode disassembly, if requested.
   if (options_.disassemble_) {
-    DumpBytecodes(header, idx, code, code_offset);
+    DumpBytecodes(idx, code, code_offset);
   }
 
   // Try-catch blocks.
@@ -1128,14 +1109,13 @@
 /*
  * Dumps a method.
  */
-static void DumpMethod(dex_ir::Header* header, uint32_t idx, uint32_t flags,
-                       const dex_ir::CodeItem* code, int i) {
+void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i) {
   // Bail for anything private if export only requested.
   if (options_.exports_only_ && (flags & (kAccPublic | kAccProtected)) == 0) {
     return;
   }
 
-  dex_ir::MethodId* method_id = header->GetCollections().GetMethodId(idx);
+  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();
@@ -1150,7 +1130,7 @@
       fprintf(out_file_, "      code          : (none)\n");
     } else {
       fprintf(out_file_, "      code          -\n");
-      DumpCode(header, idx, code, code->GetOffset());
+      DumpCode(idx, code, code->GetOffset());
     }
     if (options_.disassemble_) {
       fputc('\n', out_file_);
@@ -1233,14 +1213,13 @@
 /*
  * Dumps a static (class) field.
  */
-static void DumpSField(dex_ir::Header* header, uint32_t idx, uint32_t flags,
-                       int i, dex_ir::EncodedValue* init) {
+void DexLayout::DumpSField(uint32_t idx, uint32_t flags, 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->GetCollections().GetFieldId(idx);
+  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();
@@ -1281,8 +1260,8 @@
 /*
  * Dumps an instance field.
  */
-static void DumpIField(dex_ir::Header* header, uint32_t idx, uint32_t flags, int i) {
-  DumpSField(header, idx, flags, i, nullptr);
+void DexLayout::DumpIField(uint32_t idx, uint32_t flags, int i) {
+  DumpSField(idx, flags, i, nullptr);
 }
 
 /*
@@ -1293,19 +1272,19 @@
  * If "*last_package" is nullptr or does not match the current class' package,
  * the value will be replaced with a newly-allocated string.
  */
-static void DumpClass(dex_ir::Header* header, int idx, char** last_package) {
-  dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
+void DexLayout::DumpClass(int idx, char** last_package) {
+  dex_ir::ClassDef* class_def = header_->GetCollections().GetClassDef(idx);
   // Omitting non-public class.
   if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) {
     return;
   }
 
   if (options_.show_section_headers_) {
-    DumpClassDef(header, idx);
+    DumpClassDef(idx);
   }
 
   if (options_.show_annotations_) {
-    DumpClassAnnotations(header, idx);
+    DumpClassAnnotations(idx);
   }
 
   // For the XML output, show the package name.  Ideally we'd gather
@@ -1313,7 +1292,7 @@
   // 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->GetCollections().GetClassDef(idx)->ClassType()->GetStringId()->Data();
+      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?
@@ -1406,8 +1385,7 @@
     dex_ir::FieldItemVector* static_fields = class_data->StaticFields();
     if (static_fields != nullptr) {
       for (uint32_t i = 0; i < static_fields->size(); i++) {
-        DumpSField(header,
-                   (*static_fields)[i]->GetFieldId()->GetIndex(),
+        DumpSField((*static_fields)[i]->GetFieldId()->GetIndex(),
                    (*static_fields)[i]->GetAccessFlags(),
                    i,
                    i < encoded_values_size ? (*encoded_values)[i].get() : nullptr);
@@ -1423,8 +1401,7 @@
     dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields();
     if (instance_fields != nullptr) {
       for (uint32_t i = 0; i < instance_fields->size(); i++) {
-        DumpIField(header,
-                   (*instance_fields)[i]->GetFieldId()->GetIndex(),
+        DumpIField((*instance_fields)[i]->GetFieldId()->GetIndex(),
                    (*instance_fields)[i]->GetAccessFlags(),
                    i);
       }  // for
@@ -1439,8 +1416,7 @@
     dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods();
     if (direct_methods != nullptr) {
       for (uint32_t i = 0; i < direct_methods->size(); i++) {
-        DumpMethod(header,
-                   (*direct_methods)[i]->GetMethodId()->GetIndex(),
+        DumpMethod((*direct_methods)[i]->GetMethodId()->GetIndex(),
                    (*direct_methods)[i]->GetAccessFlags(),
                    (*direct_methods)[i]->GetCodeItem(),
                  i);
@@ -1456,8 +1432,7 @@
     dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods();
     if (virtual_methods != nullptr) {
       for (uint32_t i = 0; i < virtual_methods->size(); i++) {
-        DumpMethod(header,
-                   (*virtual_methods)[i]->GetMethodId()->GetIndex(),
+        DumpMethod((*virtual_methods)[i]->GetMethodId()->GetIndex(),
                    (*virtual_methods)[i]->GetAccessFlags(),
                    (*virtual_methods)[i]->GetCodeItem(),
                    i);
@@ -1481,24 +1456,10 @@
   free(access_str);
 }
 
-/*
- * Dumps the requested sections of the file.
- */
-static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) {
-  if (options_.verbose_) {
-    fprintf(out_file_, "Opened '%s', DEX version '%.3s'\n",
-            file_name, dex_file->GetHeader().magic_ + 4);
-  }
-  std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
-
-  if (options_.visualize_pattern_) {
-    VisualizeDexLayout(header.get(), dex_file, dex_file_index);
-    return;
-  }
-
+void DexLayout::DumpDexFile() {
   // Headers.
   if (options_.show_file_headers_) {
-    DumpFileHeader(header.get());
+    DumpFileHeader();
   }
 
   // Open XML context.
@@ -1508,9 +1469,9 @@
 
   // Iterate over all classes.
   char* package = nullptr;
-  const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
+  const uint32_t class_defs_size = header_->GetCollections().ClassDefsSize();
   for (uint32_t i = 0; i < class_defs_size; i++) {
-    DumpClass(header.get(), i, &package);
+    DumpClass(i, &package);
   }  // for
 
   // Free the last package allocated.
@@ -1523,20 +1484,227 @@
   if (options_.output_format_ == kOutputXml) {
     fprintf(out_file_, "</api>\n");
   }
+}
 
-  // Output dex file.
-  if (options_.output_dex_directory_ != nullptr) {
+std::vector<dex_ir::ClassDef*> DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) {
+  std::vector<dex_ir::ClassDef*> new_class_def_order;
+  for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
+    dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
+    if (info_->ContainsClass(*dex_file, type_idx)) {
+      new_class_def_order.push_back(class_def.get());
+    }
+  }
+  for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
+    dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
+    if (!info_->ContainsClass(*dex_file, type_idx)) {
+      new_class_def_order.push_back(class_def.get());
+    }
+  }
+  uint32_t class_defs_offset = header_->GetCollections().ClassDefsOffset();
+  uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset();
+  for (uint32_t i = 0; i < new_class_def_order.size(); ++i) {
+    dex_ir::ClassDef* class_def = new_class_def_order[i];
+    class_def->SetIndex(i);
+    class_def->SetOffset(class_defs_offset);
+    class_defs_offset += dex_ir::ClassDef::ItemSize();
+    if (class_def->GetClassData() != nullptr) {
+      class_def->GetClassData()->SetOffset(class_data_offset);
+      class_data_offset += class_def->GetClassData()->GetSize();
+    }
+  }
+  return new_class_def_order;
+}
+
+int32_t DexLayout::LayoutCodeItems(std::vector<dex_ir::ClassDef*> new_class_def_order) {
+  int32_t diff = 0;
+  uint32_t offset = header_->GetCollections().CodeItemsOffset();
+  for (dex_ir::ClassDef* class_def : new_class_def_order) {
+    dex_ir::ClassData* class_data = class_def->GetClassData();
+    if (class_data != nullptr) {
+      class_data->SetOffset(class_data->GetOffset() + diff);
+      for (auto& method : *class_data->DirectMethods()) {
+        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        if (code_item != nullptr) {
+          diff += UnsignedLeb128Size(offset) - UnsignedLeb128Size(code_item->GetOffset());
+          code_item->SetOffset(offset);
+          offset += RoundUp(code_item->GetSize(), 4);
+        }
+      }
+      for (auto& method : *class_data->VirtualMethods()) {
+        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        if (code_item != nullptr) {
+          diff += UnsignedLeb128Size(offset) - UnsignedLeb128Size(code_item->GetOffset());
+          code_item->SetOffset(offset);
+          offset += RoundUp(code_item->GetSize(), 4);
+        }
+      }
+    }
+  }
+
+  return diff;
+}
+
+// Adjust offsets of every item in the specified section by diff bytes.
+template<class T> void DexLayout::FixupSection(std::map<uint32_t, std::unique_ptr<T>>& map,
+                                               uint32_t diff) {
+  for (auto& pair : map) {
+    std::unique_ptr<T>& item = pair.second;
+    item->SetOffset(item->GetOffset() + diff);
+  }
+}
+
+// Adjust offsets of all sections with an address after the specified offset by diff bytes.
+void DexLayout::FixupSections(uint32_t offset, uint32_t diff) {
+  dex_ir::Collections& collections = header_->GetCollections();
+  uint32_t map_list_offset = collections.MapListOffset();
+  if (map_list_offset > offset) {
+    collections.SetMapListOffset(map_list_offset + diff);
+  }
+
+  uint32_t type_lists_offset = collections.TypeListsOffset();
+  if (type_lists_offset > offset) {
+    collections.SetTypeListsOffset(type_lists_offset + diff);
+    FixupSection(collections.TypeLists(), diff);
+  }
+
+  uint32_t annotation_set_ref_lists_offset = collections.AnnotationSetRefListsOffset();
+  if (annotation_set_ref_lists_offset > offset) {
+    collections.SetAnnotationSetRefListsOffset(annotation_set_ref_lists_offset + diff);
+    FixupSection(collections.AnnotationSetRefLists(), diff);
+  }
+
+  uint32_t annotation_set_items_offset = collections.AnnotationSetItemsOffset();
+  if (annotation_set_items_offset > offset) {
+    collections.SetAnnotationSetItemsOffset(annotation_set_items_offset + diff);
+    FixupSection(collections.AnnotationSetItems(), diff);
+  }
+
+  uint32_t class_datas_offset = collections.ClassDatasOffset();
+  if (class_datas_offset > offset) {
+    collections.SetClassDatasOffset(class_datas_offset + diff);
+    FixupSection(collections.ClassDatas(), diff);
+  }
+
+  uint32_t code_items_offset = collections.CodeItemsOffset();
+  if (code_items_offset > offset) {
+    collections.SetCodeItemsOffset(code_items_offset + diff);
+    FixupSection(collections.CodeItems(), diff);
+  }
+
+  uint32_t string_datas_offset = collections.StringDatasOffset();
+  if (string_datas_offset > offset) {
+    collections.SetStringDatasOffset(string_datas_offset + diff);
+    FixupSection(collections.StringDatas(), diff);
+  }
+
+  uint32_t debug_info_items_offset = collections.DebugInfoItemsOffset();
+  if (debug_info_items_offset > offset) {
+    collections.SetDebugInfoItemsOffset(debug_info_items_offset + diff);
+    FixupSection(collections.DebugInfoItems(), diff);
+  }
+
+  uint32_t annotation_items_offset = collections.AnnotationItemsOffset();
+  if (annotation_items_offset > offset) {
+    collections.SetAnnotationItemsOffset(annotation_items_offset + diff);
+    FixupSection(collections.AnnotationItems(), diff);
+  }
+
+  uint32_t encoded_array_items_offset = collections.EncodedArrayItemsOffset();
+  if (encoded_array_items_offset > offset) {
+    collections.SetEncodedArrayItemsOffset(encoded_array_items_offset + diff);
+    FixupSection(collections.EncodedArrayItems(), diff);
+  }
+
+  uint32_t annotations_directory_items_offset = collections.AnnotationsDirectoryItemsOffset();
+  if (annotations_directory_items_offset > offset) {
+    collections.SetAnnotationsDirectoryItemsOffset(annotations_directory_items_offset + diff);
+    FixupSection(collections.AnnotationsDirectoryItems(), diff);
+  }
+}
+
+void DexLayout::LayoutOutputFile(const DexFile* dex_file) {
+  std::vector<dex_ir::ClassDef*> new_class_def_order = LayoutClassDefsAndClassData(dex_file);
+  int32_t diff = LayoutCodeItems(new_class_def_order);
+  // Adjust diff to be 4-byte aligned.
+  diff = RoundUp(diff, 4);
+  // Move sections after ClassData by diff bytes.
+  FixupSections(header_->GetCollections().ClassDatasOffset(), diff);
+  // Update file size.
+  header_->SetFileSize(header_->FileSize() + diff);
+}
+
+void DexLayout::OutputDexFile(const std::string& dex_file_location) {
+  std::string error_msg;
+  std::unique_ptr<File> new_file;
+  if (!options_.output_to_memmap_) {
     std::string output_location(options_.output_dex_directory_);
-    size_t last_slash = dex_file->GetLocation().rfind('/');
-    output_location.append(dex_file->GetLocation().substr(last_slash));
-    DexWriter::OutputDexFile(*header, output_location.c_str());
+    size_t last_slash = dex_file_location.rfind("/");
+    std::string dex_file_directory = dex_file_location.substr(0, last_slash + 1);
+    if (output_location == dex_file_directory) {
+      output_location = dex_file_location + ".new";
+    } else if (last_slash != std::string::npos) {
+      output_location += dex_file_location.substr(last_slash);
+    } else {
+      output_location += "/" + dex_file_location + ".new";
+    }
+    new_file.reset(OS::CreateEmptyFile(output_location.c_str()));
+    ftruncate(new_file->Fd(), header_->FileSize());
+    mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED,
+        new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg));
+  } else {
+    mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, header_->FileSize(),
+        PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, &error_msg));
+  }
+  if (mem_map_ == nullptr) {
+    LOG(ERROR) << "Could not create mem map for dex writer output: " << error_msg;
+    if (new_file.get() != nullptr) {
+      new_file->Erase();
+    }
+    return;
+  }
+  DexWriter::Output(header_, mem_map_.get());
+  if (new_file != nullptr) {
+    UNUSED(new_file->FlushCloseOrErase());
+  }
+}
+
+/*
+ * Dumps the requested sections of the file.
+ */
+void DexLayout::ProcessDexFile(const char* file_name,
+                               const DexFile* dex_file,
+                               size_t dex_file_index) {
+  std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
+  SetHeader(header.get());
+
+  if (options_.verbose_) {
+    fprintf(out_file_, "Opened '%s', DEX version '%.3s'\n",
+            file_name, dex_file->GetHeader().magic_ + 4);
+  }
+
+  if (options_.visualize_pattern_) {
+    VisualizeDexLayout(header_, dex_file, dex_file_index, info_);
+    return;
+  }
+
+  // Dump dex file.
+  if (options_.dump_) {
+    DumpDexFile();
+  }
+
+  // Output dex file as file or memmap.
+  if (options_.output_dex_directory_ != nullptr || options_.output_to_memmap_) {
+    if (info_ != nullptr) {
+      LayoutOutputFile(dex_file);
+    }
+    OutputDexFile(dex_file->GetLocation());
   }
 }
 
 /*
  * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
  */
-int ProcessFile(const char* file_name) {
+int DexLayout::ProcessFile(const char* file_name) {
   if (options_.verbose_) {
     fprintf(out_file_, "Processing '%s'...\n", file_name);
   }
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index a5bd992..ac1a4a6 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -26,8 +26,13 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#include "dex_ir.h"
+#include "mem_map.h"
+
 namespace art {
 
+class DexFile;
+class Instruction;
 class ProfileCompilationInfo;
 
 /* Supported output formats. */
@@ -37,28 +42,90 @@
 };
 
 /* Command-line options. */
-struct Options {
-  bool build_dex_ir_;
-  bool checksum_only_;
-  bool disassemble_;
-  bool exports_only_;
-  bool ignore_bad_checksum_;
-  bool show_annotations_;
-  bool show_file_headers_;
-  bool show_section_headers_;
-  bool verbose_;
-  bool visualize_pattern_;
-  OutputFormat output_format_;
-  const char* output_dex_directory_;
-  const char* output_file_name_;
-  const char* profile_file_name_;
+class Options {
+ public:
+  Options() = default;
+
+  bool dump_ = false;
+  bool build_dex_ir_ = false;
+  bool checksum_only_ = false;
+  bool disassemble_ = false;
+  bool exports_only_ = false;
+  bool ignore_bad_checksum_ = false;
+  bool output_to_memmap_ = false;
+  bool show_annotations_ = false;
+  bool show_file_headers_ = false;
+  bool show_section_headers_ = false;
+  bool verbose_ = false;
+  bool visualize_pattern_ = false;
+  OutputFormat output_format_ = kOutputPlain;
+  const char* output_dex_directory_ = nullptr;
+  const char* output_file_name_ = nullptr;
+  const char* profile_file_name_ = nullptr;
 };
 
-/* Prototypes. */
-extern struct Options options_;
-extern FILE* out_file_;
-extern ProfileCompilationInfo* profile_info_;
-int ProcessFile(const char* file_name);
+class DexLayout {
+ public:
+  DexLayout(Options& options,
+            ProfileCompilationInfo* info,
+            FILE* out_file,
+            dex_ir::Header*
+            header = nullptr)
+      : options_(options), info_(info), out_file_(out_file), header_(header) { }
+
+  int ProcessFile(const char* file_name);
+  void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index);
+
+  dex_ir::Header* GetHeader() const { return header_; }
+  void SetHeader(dex_ir::Header* header) { header_ = header; }
+
+  MemMap* GetAndReleaseMemMap() { return mem_map_.release(); }
+
+ private:
+  void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item);
+  void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset);
+  void DumpCatches(const dex_ir::CodeItem* code);
+  void DumpClass(int idx, char** last_package);
+  void DumpClassAnnotations(int idx);
+  void DumpClassDef(int idx);
+  void DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset);
+  void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation);
+  void DumpEncodedValue(const dex_ir::EncodedValue* data);
+  void DumpFileHeader();
+  void DumpIField(uint32_t idx, uint32_t flags, int i);
+  void DumpInstruction(const dex_ir::CodeItem* code,
+                       uint32_t code_offset,
+                       uint32_t insn_idx,
+                       uint32_t insn_width,
+                       const Instruction* dec_insn);
+  void DumpInterface(const dex_ir::TypeId* type_item, int i);
+  void DumpLocalInfo(const dex_ir::CodeItem* code);
+  void DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i);
+  void DumpPositionInfo(const dex_ir::CodeItem* code);
+  void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init);
+  void DumpDexFile();
+
+  std::vector<dex_ir::ClassDef*> LayoutClassDefsAndClassData(const DexFile* dex_file);
+  int32_t LayoutCodeItems(std::vector<dex_ir::ClassDef*> new_class_def_order);
+  template<class T> void FixupSection(std::map<uint32_t, std::unique_ptr<T>>& map, uint32_t diff);
+  void FixupSections(uint32_t offset, uint32_t diff);
+
+  // Creates a new layout for the dex file based on profile info.
+  // Currently reorders ClassDefs, ClassDataItems, and CodeItems.
+  void LayoutOutputFile(const DexFile* dex_file);
+  void OutputDexFile(const std::string& dex_file_location);
+
+  void DumpCFG(const DexFile* dex_file, int idx);
+  void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code);
+
+  Options& options_;
+  ProfileCompilationInfo* info_;
+  FILE* out_file_;
+  dex_ir::Header* header_;
+  std::unique_ptr<MemMap> mem_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(DexLayout);
+};
 
 }  // namespace art
 
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 825dd50..5f8a118 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -68,64 +68,67 @@
   InitLogging(argv, Runtime::Aborter);
   MemMap::Init();
 
-  // Reset options.
+  Options options;
+  options.dump_ = true;
+  options.verbose_ = true;
   bool want_usage = false;
-  memset(&options_, 0, sizeof(options_));
-  options_.verbose_ = true;
 
   // Parse all arguments.
   while (1) {
-    const int ic = getopt(argc, argv, "abcdefghil:o:p:sw:");
+    const int ic = getopt(argc, argv, "abcdefghil:mo:p:sw:");
     if (ic < 0) {
       break;  // done
     }
     switch (ic) {
       case 'a':  // display annotations
-        options_.show_annotations_ = true;
+        options.show_annotations_ = true;
         break;
       case 'b':  // build dex_ir
-        options_.build_dex_ir_ = true;
+        options.build_dex_ir_ = true;
         break;
       case 'c':  // verify the checksum then exit
-        options_.checksum_only_ = true;
+        options.checksum_only_ = true;
         break;
       case 'd':  // disassemble Dalvik instructions
-        options_.disassemble_ = true;
+        options.disassemble_ = true;
         break;
       case 'e':  // exported items only
-        options_.exports_only_ = true;
+        options.exports_only_ = true;
         break;
       case 'f':  // display outer file header
-        options_.show_file_headers_ = true;
+        options.show_file_headers_ = true;
         break;
       case 'h':  // display section headers, i.e. all meta-data
-        options_.show_section_headers_ = true;
+        options.show_section_headers_ = true;
         break;
       case 'i':  // continue even if checksum is bad
-        options_.ignore_bad_checksum_ = true;
+        options.ignore_bad_checksum_ = true;
         break;
       case 'l':  // layout
         if (strcmp(optarg, "plain") == 0) {
-          options_.output_format_ = kOutputPlain;
+          options.output_format_ = kOutputPlain;
         } else if (strcmp(optarg, "xml") == 0) {
-          options_.output_format_ = kOutputXml;
-          options_.verbose_ = false;
+          options.output_format_ = kOutputXml;
+          options.verbose_ = false;
         } else {
           want_usage = true;
         }
         break;
+      case 'm':  // output dex files to a memmap
+        options.output_to_memmap_ = true;
+        break;
       case 'o':  // output file
-        options_.output_file_name_ = optarg;
+        options.output_file_name_ = optarg;
         break;
       case 'p':  // profile file
-        options_.profile_file_name_ = optarg;
+        options.profile_file_name_ = optarg;
         break;
       case 's':  // visualize access pattern
-        options_.visualize_pattern_ = true;
-        options_.verbose_ = false;
+        options.visualize_pattern_ = true;
+        options.verbose_ = false;
         break;
       case 'w':  // output dex files directory
-        options_.output_dex_directory_ = optarg;
+        options.output_dex_directory_ = optarg;
         break;
       default:
         want_usage = true;
@@ -138,7 +141,7 @@
     fprintf(stderr, "%s: no file specified\n", kProgramName);
     want_usage = true;
   }
-  if (options_.checksum_only_ && options_.ignore_bad_checksum_) {
+  if (options.checksum_only_ && options.ignore_bad_checksum_) {
     fprintf(stderr, "Can't specify both -c and -i\n");
     want_usage = true;
   }
@@ -148,32 +151,37 @@
   }
 
   // Open alternative output file.
-  if (options_.output_file_name_) {
-    out_file_ = fopen(options_.output_file_name_, "w");
-    if (!out_file_) {
-      fprintf(stderr, "Can't open %s\n", options_.output_file_name_);
+  FILE* out_file = stdout;
+  if (options.output_file_name_) {
+    out_file = fopen(options.output_file_name_, "w");
+    if (!out_file) {
+      fprintf(stderr, "Can't open %s\n", options.output_file_name_);
       return 1;
     }
   }
 
   // Open profile file.
-  if (options_.profile_file_name_) {
-    int profile_fd = open(options_.profile_file_name_, O_RDONLY);
+  ProfileCompilationInfo* profile_info = nullptr;
+  if (options.profile_file_name_) {
+    int profile_fd = open(options.profile_file_name_, O_RDONLY);
     if (profile_fd < 0) {
-      fprintf(stderr, "Can't open %s\n", options_.profile_file_name_);
+      fprintf(stderr, "Can't open %s\n", options.profile_file_name_);
       return 1;
     }
-    profile_info_ = new ProfileCompilationInfo();
-    if (!profile_info_->Load(profile_fd)) {
-      fprintf(stderr, "Can't read profile info from %s\n", options_.profile_file_name_);
+    profile_info = new ProfileCompilationInfo();
+    if (!profile_info->Load(profile_fd)) {
+      fprintf(stderr, "Can't read profile info from %s\n", options.profile_file_name_);
       return 1;
     }
   }
 
+  // Create DexLayout instance.
+  DexLayout dex_layout(options, profile_info, out_file);
+
   // Process all files supplied on command line.
   int result = 0;
   while (optind < argc) {
-    result |= ProcessFile(argv[optind++]);
+    result |= dex_layout.ProcessFile(argv[optind++]);
   }  // while
   return result != 0;
 }
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index c7f36be..665baa6 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -22,11 +22,57 @@
 #include <unistd.h>
 
 #include "base/stringprintf.h"
+#include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
 #include "utils.h"
 
 namespace art {
 
+static const char kDexFileLayoutInputDex[] =
+    "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH"
+    "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB"
+    "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA"
+    "AAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAdQEAAAAAAAABAAAA"
+    "AAAAAAIAAAAAAAAAAgAAAAAAAAB/AQAAAAAAAAEAAQABAAAAaQEAAAQAAABwEAIAAAAOAAEAAQAB"
+    "AAAAbwEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph"
+    "dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQAAgIAEgAIAAAEAAYCABJgCAAAACwAA"
+    "AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA"
+    "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC"
+    "AAAAdQEAAAAQAAABAAAAjAEAAA==";
+
+static const char kDexFileLayoutInputProfile[] =
+    "cHJvADAwMgABAAsAAAABAPUpbf5jbGFzc2VzLmRleAEA";
+
+static const char kDexFileLayoutExpectedOutputDex[] =
+    "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH"
+    "AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB"
+    "AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA"
+    "AAAAAAABAAAAAAAAAAIAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAIAAAAAAAAAdQEAAAAAAAAAAAAA"
+    "AAAAAAIAAAAAAAAAAQAAAAAAAAB/AQAAAAAAAAEAAQABAAAAbwEAAAQAAABwEAIAAAAOAAEAAQAB"
+    "AAAAaQEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph"
+    "dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQABgIAEgAIAAAEAAICABJgCAAAACwAA"
+    "AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA"
+    "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC"
+    "AAAAdQEAAAAQAAABAAAAjAEAAA==";
+
+static void WriteFileBase64(const char* base64, const char* location) {
+  // Decode base64.
+  CHECK(base64 != nullptr);
+  size_t length;
+  std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
+  CHECK(bytes.get() != nullptr);
+
+  // Write to provided file.
+  std::unique_ptr<File> file(OS::CreateEmptyFile(location));
+  CHECK(file.get() != nullptr);
+  if (!file->WriteFully(bytes.get(), length)) {
+    PLOG(FATAL) << "Failed to write base64 as file";
+  }
+  if (file->FlushCloseOrErase() != 0) {
+    PLOG(FATAL) << "Could not flush and close test file.";
+  }
+}
+
 class DexLayoutTest : public CommonRuntimeTest {
  protected:
   virtual void SetUp() {
@@ -51,7 +97,6 @@
           { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file };
       std::vector<std::string> dexlayout_exec_argv =
           { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file };
-
       if (!::art::Exec(dexdump_exec_argv, error_msg)) {
         return false;
       }
@@ -78,13 +123,11 @@
 
     for (const std::string &dex_file : GetLibCoreDexFileNames()) {
       std::vector<std::string> dexlayout_exec_argv =
-          { dexlayout, "-d", "-f", "-h", "-l", "plain", "-w", tmp_dir, "-o", tmp_name, dex_file };
-
+          { dexlayout, "-w", tmp_dir, "-o", tmp_name, dex_file };
       if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
         return false;
       }
-
-      size_t dex_file_last_slash = dex_file.rfind('/');
+      size_t dex_file_last_slash = dex_file.rfind("/");
       std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1);
       std::vector<std::string> unzip_exec_argv =
           { "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir};
@@ -105,7 +148,44 @@
         return false;
       }
     }
+    return true;
+  }
 
+  // Runs DexFileOutput test.
+  bool DexFileLayoutExec(std::string* error_msg) {
+    ScratchFile tmp_file;
+    std::string tmp_name = tmp_file.GetFilename();
+    size_t tmp_last_slash = tmp_name.rfind("/");
+    std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+
+    // Write inputs and expected outputs.
+    std::string dex_file = tmp_dir + "classes.dex";
+    WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str());
+    std::string profile_file = tmp_dir + "primary.prof";
+    WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str());
+    std::string expected_output = tmp_dir + "expected.dex";
+    WriteFileBase64(kDexFileLayoutExpectedOutputDex, expected_output.c_str());
+    std::string output_dex = tmp_dir + "classes.dex.new";
+
+    std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
+    EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
+
+    std::vector<std::string> dexlayout_exec_argv =
+    { dexlayout, "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file };
+    if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
+      return false;
+    }
+    std::vector<std::string> diff_exec_argv =
+        { "/usr/bin/diff", expected_output, output_dex };
+    if (!::art::Exec(diff_exec_argv, error_msg)) {
+      return false;
+    }
+
+    std::vector<std::string> rm_exec_argv =
+        { "/bin/rm", dex_file, profile_file, expected_output, output_dex };
+    if (!::art::Exec(rm_exec_argv, error_msg)) {
+      return false;
+    }
     return true;
   }
 };
@@ -125,4 +205,11 @@
   ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg;
 }
 
+TEST_F(DexLayoutTest, DexFileLayout) {
+  // Disable test on target.
+  TEST_DISABLED_FOR_TARGET();
+  std::string error_msg;
+  ASSERT_TRUE(DexFileLayoutExec(&error_msg)) << error_msg;
+}
+
 }  // namespace art
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 30b708c..3347dac 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -63,9 +63,7 @@
         case kVld2Location:
         case kVld3Location:
         case kVld4Location: {
-          const uintptr_t pc_delta = disasm_->IsT32()
-              ? vixl::aarch32::kT32PcDelta
-              : vixl::aarch32::kA32PcDelta;
+          const uintptr_t pc_delta = label.GetLabel()->GetPcOffset();
           const int32_t offset = label.GetLabel()->GetLocation();
 
           os() << "[pc, #" << offset - pc_delta << "]";
@@ -77,7 +75,7 @@
       }
     }
 
-    DisassemblerStream& operator<<(const vixl::aarch32::Register reg) OVERRIDE {
+    DisassemblerStream& operator<<(vixl::aarch32::Register reg) OVERRIDE {
       if (reg.Is(tr)) {
         os() << "tr";
         return *this;
@@ -118,20 +116,11 @@
   CustomDisassembler(std::ostream& os, const DisassemblerOptions* options)
       : PrintDisassembler(&disassembler_stream_), disassembler_stream_(os, this, options) {}
 
-  void PrintPc(uint32_t prog_ctr) OVERRIDE {
+  void PrintCodeAddress(uint32_t prog_ctr) OVERRIDE {
     os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": ";
   }
 
-  bool IsT32() const {
-    return is_t32_;
-  }
-
-  void SetT32(bool is_t32) {
-    is_t32_ = is_t32;
-  }
-
  private:
-  bool is_t32_;
   CustomDisassemblerStream disassembler_stream_;
 };
 
@@ -152,7 +141,7 @@
       sizeof(unaligned_float), sizeof(unaligned_double)};
   const uintptr_t begin = reinterpret_cast<uintptr_t>(options_->base_address_);
   const uintptr_t end = reinterpret_cast<uintptr_t>(options_->end_address_);
-  uintptr_t literal_addr = RoundDown(disasm_->GetPc(), vixl::aarch32::kRegSizeInBytes) + offset;
+  uintptr_t literal_addr = RoundDown(disasm_->GetCodeAddress(), vixl::aarch32::kRegSizeInBytes) + offset;
 
   if (!options_->absolute_addresses_) {
     literal_addr += begin;
@@ -208,12 +197,14 @@
   // Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
   const uintptr_t instr_ptr = reinterpret_cast<uintptr_t>(begin) & ~1;
 
-  disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
-  disasm_->JumpToPc(GetPc(instr_ptr));
+  const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+  disasm_->SetCodeAddress(GetPc(instr_ptr));
 
-  if (disasm_->IsT32()) {
+  if (is_t32) {
     const uint16_t* const ip = reinterpret_cast<const uint16_t*>(instr_ptr);
-    next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip));
+    const uint16_t* const end_address = reinterpret_cast<const uint16_t*>(
+        GetDisassemblerOptions()->end_address_);
+    next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip, end_address));
   } else {
     const uint32_t* const ip = reinterpret_cast<const uint32_t*>(instr_ptr);
     next = reinterpret_cast<uintptr_t>(disasm_->DecodeA32At(ip));
@@ -230,10 +221,10 @@
   // Remove the Thumb specifier bit; no effect if begin does not point to T32 code.
   const uintptr_t base = reinterpret_cast<uintptr_t>(begin) & ~1;
 
-  disasm_->SetT32((reinterpret_cast<uintptr_t>(begin) & 1) != 0);
-  disasm_->JumpToPc(GetPc(base));
+  const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0;
+  disasm_->SetCodeAddress(GetPc(base));
 
-  if (disasm_->IsT32()) {
+  if (is_t32) {
     // The Thumb specifier bits cancel each other.
     disasm_->DisassembleT32Buffer(reinterpret_cast<const uint16_t*>(base), end - begin);
   } else {
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index bbe6cc1..f1fcf3d 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -54,13 +54,22 @@
 
 art_cc_binary {
     name: "oatdumps",
-    defaults: ["oatdump-defaults"],
     device_supported: false,
+    static_executable: true,
+    defaults: ["oatdump-defaults"],
     target: {
         darwin: {
             enabled: false,
         },
     },
+    ldflags: [
+        // 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.
+        "-z muldefs",
+    ],
     static_libs: [
         "libart",
         "libart-compiler",
@@ -72,16 +81,25 @@
 
 art_cc_binary {
     name: "oatdumpds",
+    device_supported: false,
+    static_executable: true,
     defaults: [
         "art_debug_defaults",
         "oatdump-defaults",
     ],
-    device_supported: false,
     target: {
         darwin: {
             enabled: false,
         },
     },
+    ldflags: [
+        // 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.
+        "-z muldefs",
+    ],
     static_libs: [
         "libartd",
         "libartd-compiler",
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 3d208b5..3ad0f1e 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -794,7 +794,7 @@
       uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index);
       const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index);
       os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)",
-                         class_def_index, descriptor, oat_class_offset, class_def.class_idx_)
+                         class_def_index, descriptor, oat_class_offset, class_def.class_idx_.index_)
          << " (" << oat_class.GetStatus() << ")"
          << " (" << oat_class.GetType() << ")\n";
       // TODO: include bitmap here if type is kOatClassSomeCompiled?
@@ -2783,7 +2783,7 @@
 
     bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size);
 
-    if (klass->GetIfTable() == nullptr) {
+    if (klass->GetIfTable()->Count() == 0) {
       DCHECK(result);
     }
 
@@ -2889,25 +2889,23 @@
     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 (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 class_hash, name_hash, signature_hash;
-          ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
-          uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
-          std::cerr << "    " << iface_method.PrettyMethod(true)
-              << " slot=" << imt_slot
-              << std::hex
-              << " class_hash=0x" << class_hash
-              << " name_hash=0x" << name_hash
-              << " signature_hash=0x" << signature_hash
-              << std::dec
-              << std::endl;
-        }
+      for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
+        uint32_t class_hash, name_hash, signature_hash;
+        ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash);
+        uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
+        std::cerr << "    " << iface_method.PrettyMethod(true)
+            << " slot=" << imt_slot
+            << std::hex
+            << " class_hash=0x" << class_hash
+            << " name_hash=0x" << name_hash
+            << " signature_hash=0x" << signature_hash
+            << std::dec
+            << std::endl;
       }
     }
   }
@@ -2972,18 +2970,16 @@
         } 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 = iface_method.PrettyMethod(true);
-                    if (StartsWith(i_name, method.c_str())) {
-                      std::cerr << "  Slot " << index << " (1)" << std::endl;
-                      std::cerr << "    " << p_name << " (" << i_name << ")" << std::endl;
-                    }
+          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 = iface_method.PrettyMethod(true);
+                  if (StartsWith(i_name, method.c_str())) {
+                    std::cerr << "  Slot " << index << " (1)" << std::endl;
+                    std::cerr << "    " << p_name << " (" << i_name << ")" << std::endl;
                   }
                 }
               }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 3c8c1a3..db28a3f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -498,7 +498,7 @@
   return true;
 }
 
-class PatchOatArtFieldVisitor : public ArtFieldVisitor {
+class PatchOat::PatchOatArtFieldVisitor : public ArtFieldVisitor {
  public:
   explicit PatchOatArtFieldVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {}
 
@@ -517,7 +517,7 @@
   image_header->VisitPackedArtFields(&visitor, heap_->Begin());
 }
 
-class PatchOatArtMethodVisitor : public ArtMethodVisitor {
+class PatchOat::PatchOatArtMethodVisitor : public ArtMethodVisitor {
  public:
   explicit PatchOatArtMethodVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {}
 
@@ -558,7 +558,7 @@
       pointer_size);
 }
 
-class FixupRootVisitor : public RootVisitor {
+class PatchOat::FixupRootVisitor : public RootVisitor {
  public:
   explicit FixupRootVisitor(const PatchOat* patch_oat) : patch_oat_(patch_oat) {
   }
@@ -610,7 +610,7 @@
 }
 
 
-class RelocatedPointerVisitor {
+class PatchOat::RelocatedPointerVisitor {
  public:
   explicit RelocatedPointerVisitor(PatchOat* patch_oat) : patch_oat_(patch_oat) {}
 
@@ -762,16 +762,14 @@
     if (vtable != nullptr) {
       vtable->Fixup(RelocatedCopyOfFollowImages(vtable), pointer_size, native_visitor);
     }
-    auto* iftable = klass->GetIfTable();
-    if (iftable != nullptr) {
-      for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
-        if (iftable->GetMethodArrayCount(i) > 0) {
-          auto* method_array = iftable->GetMethodArray(i);
-          CHECK(method_array != nullptr);
-          method_array->Fixup(RelocatedCopyOfFollowImages(method_array),
-                              pointer_size,
-                              native_visitor);
-        }
+    mirror::IfTable* iftable = klass->GetIfTable();
+    for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
+      if (iftable->GetMethodArrayCount(i) > 0) {
+        auto* method_array = iftable->GetMethodArray(i);
+        CHECK(method_array != nullptr);
+        method_array->Fixup(RelocatedCopyOfFollowImages(method_array),
+                            pointer_size,
+                            native_visitor);
       }
     }
   } else if (object->GetClass() == mirror::Method::StaticClass() ||
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index e7a3e91..a519631 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -230,10 +230,11 @@
 
   TimingLogger* timings_;
 
-  friend class FixupRootVisitor;
-  friend class RelocatedPointerVisitor;
-  friend class PatchOatArtFieldVisitor;
-  friend class PatchOatArtMethodVisitor;
+  class FixupRootVisitor;
+  class RelocatedPointerVisitor;
+  class PatchOatArtFieldVisitor;
+  class PatchOatArtMethodVisitor;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat);
 };
 
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index cd0aa6f..776c31a 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -42,7 +42,7 @@
       ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i));
     }
     for (uint16_t i = 0; i < number_of_classes; i++) {
-      ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, i));
+      ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, dex::TypeIndex(i)));
     }
 
     ASSERT_TRUE(info->Save(GetFd(profile)));
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 2a12f1d..de72d3a 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -86,7 +86,7 @@
   DefaultInitEntryPoints(jpoints, qpoints);
 
   // Cast
-  qpoints->pInstanceofNonTrivial = artInstanceOfFromCode;;
+  qpoints->pInstanceofNonTrivial = artInstanceOfFromCode;
   qpoints->pCheckInstanceOf = art_quick_check_instance_of;
 
   // Math
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index cb0bdbf..6a442a5 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -156,17 +156,24 @@
   // JNI
   qpoints->pJniMethodStart = JniMethodStart;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodStart), "Non-direct C stub marked direct.");
+  qpoints->pJniMethodFastStart = JniMethodFastStart;
+  static_assert(!IsDirectEntrypoint(kQuickJniMethodFastStart), "Non-direct C stub marked direct.");
   qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodStartSynchronized),
                 "Non-direct C stub marked direct.");
   qpoints->pJniMethodEnd = JniMethodEnd;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodEnd), "Non-direct C stub marked direct.");
+  qpoints->pJniMethodFastEnd = JniMethodFastEnd;
+  static_assert(!IsDirectEntrypoint(kQuickJniMethodFastEnd), "Non-direct C stub marked direct.");
   qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodEndSynchronized),
                 "Non-direct C stub marked direct.");
   qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodEndWithReference),
                 "Non-direct C stub marked direct.");
+  qpoints->pJniMethodFastEndWithReference = JniMethodFastEndWithReference;
+  static_assert(!IsDirectEntrypoint(kQuickJniMethodFastEndWithReference),
+                "Non-direct C stub marked direct.");
   qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
   static_assert(!IsDirectEntrypoint(kQuickJniMethodEndWithReferenceSynchronized),
                 "Non-direct C stub marked direct.");
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index bbf9a8b..6665897 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1063,7 +1063,7 @@
   EXPECT_FALSE(self->IsExceptionPending());
   {
     // Use an arbitrary method from c to use as referrer
-    size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
+    size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex().index_),    // type_idx
                             // arbitrary
                             reinterpret_cast<size_t>(c->GetVirtualMethod(0, kRuntimePointerSize)),
                             0U,
@@ -1197,7 +1197,7 @@
   if ((false)) {
     // Use an arbitrary method from c to use as referrer
     size_t result = Invoke3(
-        static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
+        static_cast<size_t>(c->GetDexTypeIndex().index_),    // type_idx
         10U,
         // arbitrary
         reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0, kRuntimePointerSize)),
diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc
index 077d2db..cb3dfec 100644
--- a/runtime/arch/x86/context_x86.cc
+++ b/runtime/arch/x86/context_x86.cc
@@ -17,6 +17,7 @@
 #include "context_x86.h"
 
 #include "base/bit_utils.h"
+#include "base/memory_tool.h"
 #include "quick/quick_method_frame_info.h"
 
 namespace art {
@@ -102,6 +103,7 @@
   uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - sizeof(intptr_t);
   gprs[kNumberOfCpuRegisters] = esp;
   *(reinterpret_cast<uintptr_t*>(esp)) = eip_;
+  MEMORY_TOOL_HANDLE_NO_RETURN;
   __asm__ __volatile__(
       "movl %1, %%ebx\n\t"          // Address base of FPRs.
       "movsd 0(%%ebx), %%xmm0\n\t"  // Load up XMM0-XMM7.
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index b46b058..25b8ed2 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -48,7 +48,7 @@
   return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
 }
 
-ObjPtr<mirror::Class> ArtField::ResolveGetType(uint32_t type_idx) {
+ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) {
   return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
 }
 
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 7c2f490..cacb324 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -19,6 +19,7 @@
 
 #include <jni.h>
 
+#include "dex_file_types.h"
 #include "gc_root.h"
 #include "modifiers.h"
 #include "obj_ptr.h"
@@ -216,7 +217,8 @@
  private:
   ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  ObjPtr<mirror::Class> ResolveGetType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   ObjPtr<mirror::String> ResolveGetStringName(Thread* self,
                                               const DexFile& dex_file,
                                               uint32_t string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index a652178..2dfdc16 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -183,17 +183,17 @@
 }
 
 template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index,
+inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
                                                          PointerSize pointer_size) {
   if (kWithCheck) {
     mirror::DexCache* dex_cache =
         GetInterfaceMethodIfProxy(pointer_size)->GetDeclaringClass()->GetDexCache();
-    if (UNLIKELY(type_index >= dex_cache->NumResolvedTypes())) {
-      ThrowArrayIndexOutOfBoundsException(type_index, dex_cache->NumResolvedTypes());
+    if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
+      ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
       return nullptr;
     }
   }
-  mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index].Read();
+  mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read();
   return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
 }
 
@@ -210,7 +210,7 @@
   return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
 }
 
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx,
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx,
                                                        bool resolve,
                                                        PointerSize pointer_size) {
   mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size);
@@ -336,7 +336,7 @@
   return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
 }
 
-inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx, PointerSize pointer_size) {
+inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
   DCHECK(!IsProxyMethod());
   return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr;
 }
@@ -383,11 +383,10 @@
   const DexFile* dex_file = GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
-  uint16_t return_type_idx = proto_id.return_type_idx_;
-  return dex_file->GetTypeDescriptor(dex_file->GetTypeId(return_type_idx));
+  return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_));
 }
 
-inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(uint16_t type_idx) {
+inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
   return dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_idx));
@@ -440,7 +439,7 @@
   const DexFile* dex_file = GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
-  uint16_t return_type_idx = proto_id.return_type_idx_;
+  dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
   mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size);
   if (type == nullptr && resolve) {
     type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index c550a1b..d1454b6 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -199,9 +199,9 @@
   // Iterate over the catch handlers associated with dex_pc.
   PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
-    uint16_t iter_type_idx = it.GetHandlerTypeIndex();
+    dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
     // Catch all case
-    if (iter_type_idx == DexFile::kDexNoIndex16) {
+    if (!iter_type_idx.IsValid()) {
       found_dex_pc = it.GetHandlerAddress();
       break;
     }
@@ -476,7 +476,11 @@
     }
     // The table is in the .vdex file.
     const OatFile::OatDexFile* oat_dex_file = GetDexCache()->GetDexFile()->GetOatDexFile();
-    return oat_dex_file->GetOatFile()->DexBegin() + header->vmap_table_offset_;
+    const OatFile* oat_file = oat_dex_file->GetOatFile();
+    if (oat_file == nullptr) {
+      return nullptr;
+    }
+    return oat_file->DexBegin() + header->vmap_table_offset_;
   } else {
     return oat_method.GetVmapTable();
   }
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b31999f..0e1d7e7 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -343,7 +343,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <bool kWithCheck = true>
-  mirror::Class* GetDexCacheResolvedType(uint32_t type_idx, PointerSize pointer_size)
+  mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
                                 PointerSize pointer_size)
@@ -355,7 +355,9 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Get the Class* from the type index into this method's dex cache.
-  mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve, PointerSize pointer_size)
+  mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx,
+                                       bool resolve,
+                                       PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if this method has the same name and signature of the other method.
@@ -527,7 +529,7 @@
 
   const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool IsResolvedTypeIdx(uint16_t type_idx, PointerSize pointer_size)
+  bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -544,7 +546,7 @@
 
   const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx)
+  const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 6b21a56..1dca428 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -80,7 +80,7 @@
     gCmdLine.reset(new std::string("<unset>"));
   }
 
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
 #define INIT_LOGGING_DEFAULT_LOGGER android::base::LogdLogger()
 #else
 #define INIT_LOGGING_DEFAULT_LOGGER android::base::StderrLogger
diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h
index e1a2e07..42cbaa0 100644
--- a/runtime/base/memory_tool.h
+++ b/runtime/base/memory_tool.h
@@ -40,7 +40,10 @@
 constexpr bool kMemoryToolIsAvailable = false;
 #endif
 
+extern "C" void __asan_handle_no_return();
+
 #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#define MEMORY_TOOL_HANDLE_NO_RETURN __asan_handle_no_return()
 #define RUNNING_ON_MEMORY_TOOL 1U
 constexpr bool kMemoryToolIsValgrind = false;
 constexpr bool kMemoryToolDetectsLeaks = true;
@@ -55,6 +58,7 @@
 #define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s)
 #define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
 #define ATTRIBUTE_NO_SANITIZE_ADDRESS
+#define MEMORY_TOOL_HANDLE_NO_RETURN do { } while (0)
 #define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND
 constexpr bool kMemoryToolIsAvailable = true;
 constexpr bool kMemoryToolIsValgrind = true;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 5d92298..e8ef69f 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -48,7 +48,7 @@
 Mutex* Locks::modify_ldt_lock_ = nullptr;
 MutatorMutex* Locks::mutator_lock_ = nullptr;
 Mutex* Locks::profiler_lock_ = nullptr;
-Mutex* Locks::verifier_deps_lock_ = nullptr;
+ReaderWriterMutex* Locks::verifier_deps_lock_ = nullptr;
 ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
 Mutex* Locks::host_dlopen_handles_lock_ = nullptr;
 Mutex* Locks::reference_processor_lock_ = nullptr;
@@ -672,7 +672,7 @@
   ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
   ++num_pending_readers_;
   if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) {
-    if (errno != EAGAIN) {
+    if (errno != EAGAIN && errno != EINTR) {
       PLOG(FATAL) << "futex wait failed for " << name_;
     }
   }
@@ -796,7 +796,7 @@
                    reinterpret_cast<const timespec*>(std::numeric_limits<int32_t>::max()),
                    guard_.state_.Address(), cur_sequence) != -1;
       if (!done) {
-        if (errno != EAGAIN) {
+        if (errno != EAGAIN && errno != EINTR) {
           PLOG(FATAL) << "futex cmp requeue failed for " << name_;
         }
       }
@@ -1039,7 +1039,7 @@
 
     UPDATE_CURRENT_LOCK_LEVEL(kVerifierDepsLock);
     DCHECK(verifier_deps_lock_ == nullptr);
-    verifier_deps_lock_ = new Mutex("verifier deps lock", current_lock_level);
+    verifier_deps_lock_ = new ReaderWriterMutex("verifier deps lock", current_lock_level);
 
     UPDATE_CURRENT_LOCK_LEVEL(kHostDlOpenHandlesLock);
     DCHECK(host_dlopen_handles_lock_ == nullptr);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 74b786c..7e73e0d 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -658,8 +658,8 @@
   // Guards opened oat files in OatFileManager.
   static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
 
-  // Guards verifier dependency collection in VerifierDeps.
-  static Mutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+  // Guards extra string entries for VerifierDeps.
+  static ReaderWriterMutex* verifier_deps_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
 
   // Guards dlopen_handles_ in DlOpenOatFile.
   static Mutex* host_dlopen_handles_lock_ ACQUIRED_AFTER(verifier_deps_lock_);
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index a53dcea..d5f375a 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_BASE_STL_UTIL_H_
 
 #include <algorithm>
+#include <set>
 #include <sstream>
 
 #include "base/logging.h"
@@ -187,6 +188,12 @@
   using type = T;
 };
 
+// Merge `other` entries into `to_update`.
+template <typename T>
+static inline void MergeSets(std::set<T>& to_update, const std::set<T>& other) {
+  to_update.insert(other.begin(), other.end());
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_STL_UTIL_H_
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 350855b..81adaeb 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -69,7 +69,7 @@
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
   // MethodVerifier refuses methods with string_idx out of bounds.
-  DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds());;
+  DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds());
   ObjPtr<mirror::String> string =
         mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
                                            string_idx,
@@ -86,7 +86,7 @@
   return string.Ptr();
 }
 
-inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) {
+inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> resolved_type =
       referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
@@ -103,7 +103,7 @@
   return resolved_type.Ptr();
 }
 
-inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtField* referrer) {
+inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
   ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ac5e6aa..f3aba97 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -240,6 +240,12 @@
   ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
   CHECK(cause.get() != nullptr);
 
+  // Boot classpath classes should not fail initialization.
+  if (!Runtime::Current()->IsAotCompiler()) {
+    std::string tmp;
+    CHECK(klass->GetClassLoader() != nullptr) << klass->GetDescriptor(&tmp);
+  }
+
   env->ExceptionClear();
   bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
   env->Throw(cause.get());
@@ -475,6 +481,9 @@
   SetClassRoot(kJavaLangString, java_lang_String.Get());
   SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get());
 
+  // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set.
+  java_lang_Object->SetIfTable(AllocIfTable(self, 0));
+
   // Setup the primitive type classes.
   SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean));
   SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte));
@@ -727,10 +736,12 @@
     const DexFile& dex_file = java_lang_Object->GetDexFile();
     const DexFile::TypeId* void_type_id = dex_file.FindTypeId("V");
     CHECK(void_type_id != nullptr);
-    uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
+    dex::TypeIndex void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
     // Now we resolve void type so the dex cache contains it. We use java.lang.Object class
     // as referrer so the used dex cache is core's one.
-    ObjPtr<mirror::Class> resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
+    ObjPtr<mirror::Class> resolved_type = ResolveType(dex_file,
+                                                      void_type_idx,
+                                                      java_lang_Object.Get());
     CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
     self->AssertNoPendingException();
   }
@@ -916,13 +927,11 @@
         SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces);
       }
     }
-    auto* iftable = klass->GetIfTable();
-    if (iftable != nullptr) {
-      for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
-        if (iftable->GetMethodArrayCount(i) > 0) {
-          SanityCheckArtMethodPointerArray(
-              iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces);
-        }
+    mirror::IfTable* iftable = klass->GetIfTable();
+    for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
+      if (iftable->GetMethodArrayCount(i) > 0) {
+        SanityCheckArtMethodPointerArray(
+            iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces);
       }
     }
   }
@@ -3401,7 +3410,8 @@
 }
 
 mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) {
-  ObjPtr<mirror::Class> klass = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_));
+  ObjPtr<mirror::Class> klass =
+      AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_));
   if (UNLIKELY(klass == nullptr)) {
     self->AssertPendingOOMException();
     return nullptr;
@@ -3419,10 +3429,12 @@
   ObjectLock<mirror::Class> lock(self, h_class);
   h_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
   h_class->SetPrimitiveType(type);
+  h_class->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
   mirror::Class::SetStatus(h_class, mirror::Class::kStatusInitialized, self);
   const char* descriptor = Primitive::Descriptor(type);
-  ObjPtr<mirror::Class> existing = InsertClass(descriptor, h_class.Get(),
-                                        ComputeModifiedUtf8Hash(descriptor));
+  ObjPtr<mirror::Class> existing = InsertClass(descriptor,
+                                               h_class.Get(),
+                                               ComputeModifiedUtf8Hash(descriptor));
   CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed";
   return h_class.Get();
 }
@@ -3996,7 +4008,7 @@
 
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   // In case we run without an image there won't be a backing oat file.
-  if (oat_dex_file == nullptr) {
+  if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) {
     return false;
   }
 
@@ -4086,7 +4098,7 @@
     for (; iterator.HasNext(); iterator.Next()) {
       // Ensure exception types are resolved so that they don't need resolution to be delivered,
       // unresolved exception types will be ignored by exception delivery
-      if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) {
+      if (iterator.GetHandlerTypeIndex().IsValid()) {
         ObjPtr<mirror::Class> exception_type = ResolveType(iterator.GetHandlerTypeIndex(), method);
         if (exception_type == nullptr) {
           DCHECK(Thread::Current()->IsExceptionPending());
@@ -4121,6 +4133,8 @@
   DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
   klass->SetName(soa.Decode<mirror::String>(name));
   klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache());
+  // Object has an empty iftable, copy it for that reason.
+  klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
   mirror::Class::SetStatus(klass, mirror::Class::kStatusIdx, self);
   std::string descriptor(GetDescriptorForProxy(klass.Get()));
   const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
@@ -4750,7 +4764,7 @@
   const DexFile* dex_file = m->GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(m->GetDexMethodIndex());
   const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
-  uint16_t return_type_idx = proto_id.return_type_idx_;
+  dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
   std::string return_type = dex_file->PrettyType(return_type_idx);
   std::string class_loader = mirror::Object::PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader());
   ThrowWrappedLinkageError(klass.Get(),
@@ -4768,7 +4782,7 @@
                                                    ArtMethod* method,
                                                    ArtMethod* m,
                                                    uint32_t index,
-                                                   uint32_t arg_type_idx)
+                                                   dex::TypeIndex arg_type_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(Thread::Current()->IsExceptionPending());
   DCHECK(!m->IsProxyMethod());
@@ -4858,7 +4872,7 @@
   }
   for (uint32_t i = 0; i < num_types; ++i) {
     StackHandleScope<1> hs(self);
-    uint32_t param_type_idx = types1->GetTypeItem(i).type_idx_;
+    dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
     Handle<mirror::Class> param_type(hs.NewHandle(
         method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
     if (UNLIKELY(param_type.Get() == nullptr)) {
@@ -4866,7 +4880,7 @@
                                              method1, i, param_type_idx);
       return false;
     }
-    uint32_t other_param_type_idx = types2->GetTypeItem(i).type_idx_;
+    dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
     ObjPtr<mirror::Class> other_param_type =
         method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
     if (UNLIKELY(other_param_type == nullptr)) {
@@ -5350,8 +5364,8 @@
 bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
   CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
-  uint16_t super_class_idx = class_def.superclass_idx_;
-  if (super_class_idx != DexFile::kDexNoIndex16) {
+  dex::TypeIndex super_class_idx = class_def.superclass_idx_;
+  if (super_class_idx.IsValid()) {
     // Check that a class does not inherit from itself directly.
     //
     // TODO: This is a cheap check to detect the straightforward case
@@ -5388,7 +5402,7 @@
   const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
   if (interfaces != nullptr) {
     for (size_t i = 0; i < interfaces->Size(); i++) {
-      uint16_t idx = interfaces->GetTypeItem(i).type_idx_;
+      dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_;
       ObjPtr<mirror::Class> interface = ResolveType(dex_file, idx, klass.Get());
       if (interface == nullptr) {
         DCHECK(Thread::Current()->IsExceptionPending());
@@ -6381,16 +6395,18 @@
 bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> klass,
                                             Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
   StackHandleScope<1> hs(self);
-  const size_t super_ifcount =
-      klass->HasSuperClass() ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+  const bool has_superclass = klass->HasSuperClass();
+  const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
   const bool have_interfaces = interfaces.Get() != nullptr;
   const size_t num_interfaces =
       have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
   if (num_interfaces == 0) {
     if (super_ifcount == 0) {
+      if (LIKELY(has_superclass)) {
+        klass->SetIfTable(klass->GetSuperClass()->GetIfTable());
+      }
       // Class implements no interfaces.
       DCHECK_EQ(klass->GetIfTableCount(), 0);
-      DCHECK(klass->GetIfTable() == nullptr);
       return true;
     }
     // Class implements same interfaces as parent, are any of these not marker interfaces?
@@ -6583,7 +6599,7 @@
   } else {
     // No imt in the super class, need to reconstruct from the iftable.
     ObjPtr<mirror::IfTable> if_table = super_class->GetIfTable();
-    if (if_table != nullptr) {
+    if (if_table->Count() != 0) {
       // Ignore copied methods since we will handle these in LinkInterfaceMethods.
       FillIMTFromIfTable(if_table,
                          unimplemented_method,
@@ -7502,7 +7518,7 @@
 }
 
 ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file,
-                                                      uint16_t type_idx,
+                                                      dex::TypeIndex type_idx,
                                                       ObjPtr<mirror::DexCache> dex_cache,
                                                       ObjPtr<mirror::ClassLoader> class_loader) {
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
@@ -7528,7 +7544,7 @@
 }
 
 mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
-                                        uint16_t type_idx,
+                                        dex::TypeIndex type_idx,
                                         ObjPtr<mirror::Class> referrer) {
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -7537,7 +7553,7 @@
 }
 
 mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
-                                        uint16_t type_idx,
+                                        dex::TypeIndex type_idx,
                                         Handle<mirror::DexCache> dex_cache,
                                         Handle<mirror::ClassLoader> class_loader) {
   DCHECK(dex_cache.Get() != nullptr);
@@ -7931,7 +7947,7 @@
   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();
+    const dex::TypeIndex type_idx = it.GetTypeIdx();
     param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader));
     if (param_class.Get() == nullptr) {
       DCHECK(self->IsExceptionPending());
@@ -8342,10 +8358,10 @@
                                              dex_file->GetBaseLocation(),
                                              dex_file->GetLocationChecksum());
     size_t num_resolved = 0;
-    std::unordered_set<uint16_t> class_set;
+    std::unordered_set<dex::TypeIndex> class_set;
     CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
     for (size_t i = 0; i < num_types; ++i) {
-      ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(i);
+      ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
       // Filter out null class loader since that is the boot class loader.
       if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) {
         continue;
@@ -8358,17 +8374,16 @@
       }
       ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache();
       if (klass_dex_cache == dex_cache) {
-        const size_t class_def_idx = klass->GetDexClassDefIndex();
         DCHECK(klass->IsResolved());
-        CHECK_LT(class_def_idx, num_class_defs);
-        class_set.insert(class_def_idx);
+        CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs);
+        class_set.insert(klass->GetDexTypeIndex());
       }
     }
 
     if (!class_set.empty()) {
       auto it = ret.find(resolved_classes);
       if (it != ret.end()) {
-        // Already have the key, union the class def idxs.
+        // Already have the key, union the class type indexes.
         it->AddClasses(class_set.begin(), class_set.end());
       } else {
         resolved_classes.AddClasses(class_set.begin(), class_set.end());
@@ -8411,13 +8426,8 @@
       VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with "
                      << info.GetClasses().size() << " classes";
       DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum());
-      for (uint16_t class_def_idx : info.GetClasses()) {
-        if (class_def_idx >= dex_file->NumClassDefs()) {
-          LOG(WARNING) << "Class def index " << class_def_idx << " >= " << dex_file->NumClassDefs();
-          continue;
-        }
-        const DexFile::TypeId& type_id = dex_file->GetTypeId(
-            dex_file->GetClassDef(class_def_idx).class_idx_);
+      for (dex::TypeIndex type_idx : info.GetClasses()) {
+        const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx);
         const char* descriptor = dex_file->GetTypeDescriptor(type_id);
         ret.insert(descriptor);
       }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1d29e31..9563448 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -32,6 +32,7 @@
 #include "class_table.h"
 #include "dex_cache_resolved_classes.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "gc_root.h"
 #include "jni.h"
 #include "mirror/class.h"
@@ -255,7 +256,7 @@
   // result in the DexCache. The referrer is used to identify the
   // target DexCache and ClassLoader to use for resolution.
   mirror::Class* ResolveType(const DexFile& dex_file,
-                             uint16_t type_idx,
+                             dex::TypeIndex type_idx,
                              ObjPtr<mirror::Class> referrer)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
@@ -263,18 +264,18 @@
   // Resolve a Type with the given index from the DexFile, storing the
   // result in the DexCache. The referrer is used to identify the
   // target DexCache and ClassLoader to use for resolution.
-  mirror::Class* ResolveType(uint16_t type_idx, ArtMethod* referrer)
+  mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
-  mirror::Class* ResolveType(uint16_t type_idx, ArtField* referrer)
+  mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
   // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search
   // for the type, since it may be referenced from but not contained within the given DexFile.
   ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file,
-                                           uint16_t type_idx,
+                                           dex::TypeIndex type_idx,
                                            ObjPtr<mirror::DexCache> dex_cache,
                                            ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -284,7 +285,7 @@
   // type, since it may be referenced from but not contained within
   // the given DexFile.
   mirror::Class* ResolveType(const DexFile& dex_file,
-                             uint16_t type_idx,
+                             dex::TypeIndex type_idx,
                              Handle<mirror::DexCache> dex_cache,
                              Handle<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ab2d9d0..9e17be2 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -25,6 +25,7 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "experimental_flags.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "gc/heap.h"
@@ -101,7 +102,8 @@
     EXPECT_EQ(0U, primitive->NumDirectInterfaces());
     EXPECT_FALSE(primitive->HasVTable());
     EXPECT_EQ(0, primitive->GetIfTableCount());
-    EXPECT_TRUE(primitive->GetIfTable() == nullptr);
+    EXPECT_TRUE(primitive->GetIfTable() != nullptr);
+    EXPECT_EQ(primitive->GetIfTable()->Count(), 0u);
     EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags());
   }
 
@@ -428,7 +430,7 @@
     }
     // Verify all the types referenced by this file
     for (size_t i = 0; i < dex.NumTypeIds(); i++) {
-      const DexFile::TypeId& type_id = dex.GetTypeId(i);
+      const DexFile::TypeId& type_id = dex.GetTypeId(dex::TypeIndex(i));
       const char* descriptor = dex.GetTypeDescriptor(type_id);
       AssertDexFileClass(class_loader, descriptor);
     }
@@ -890,7 +892,7 @@
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("MyClass"))));
   AssertNonExistentClass("LMyClass;");
   ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
-  uint32_t type_idx = klass->GetClassDef()->class_idx_;
+  dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
   ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
   const DexFile& dex_file = klass->GetDexFile();
   EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass);
@@ -1153,7 +1155,7 @@
   ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize);
   const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;");
   ASSERT_TRUE(type_id != nullptr);
-  uint32_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+  dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id);
   mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, soa.Self(), true, false);
   EXPECT_TRUE(uninit != nullptr);
   EXPECT_FALSE(uninit->IsInitialized());
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index b44104e..0fcce6b 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -170,7 +170,7 @@
     const DexFile* dex_file = ObjPtr<mirror::DexCache>::DownCast(obj)->GetDexFile();
     if (dex_file != nullptr && dex_file->GetOatDexFile() != nullptr) {
       const OatFile* oat_file = dex_file->GetOatDexFile()->GetOatFile();
-      if (!oat_file->GetBssGcRoots().empty()) {
+      if (oat_file != nullptr && !oat_file->GetBssGcRoots().empty()) {
         InsertOatFileLocked(oat_file);  // Ignore return value.
       }
     }
diff --git a/runtime/class_table.h b/runtime/class_table.h
index bc9eaf4..558c144 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -48,7 +48,7 @@
     uint32_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
     // Same class loader and descriptor.
     bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const
-        NO_THREAD_SAFETY_ANALYSIS;;
+        NO_THREAD_SAFETY_ANALYSIS;
     // Same descriptor.
     bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor) const
         NO_THREAD_SAFETY_ANALYSIS;
diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc
index dc197c1..6e3e1d8 100644
--- a/runtime/compiler_filter.cc
+++ b/runtime/compiler_filter.cc
@@ -33,6 +33,7 @@
     case CompilerFilter::kTime:
     case CompilerFilter::kSpeedProfile:
     case CompilerFilter::kSpeed:
+    case CompilerFilter::kLayoutProfile:
     case CompilerFilter::kEverythingProfile:
     case CompilerFilter::kEverything: return true;
   }
@@ -52,6 +53,7 @@
     case CompilerFilter::kTime:
     case CompilerFilter::kSpeedProfile:
     case CompilerFilter::kSpeed:
+    case CompilerFilter::kLayoutProfile:
     case CompilerFilter::kEverythingProfile:
     case CompilerFilter::kEverything: return true;
   }
@@ -71,6 +73,7 @@
     case CompilerFilter::kTime:
     case CompilerFilter::kSpeedProfile:
     case CompilerFilter::kSpeed:
+    case CompilerFilter::kLayoutProfile:
     case CompilerFilter::kEverythingProfile:
     case CompilerFilter::kEverything: return true;
   }
@@ -97,6 +100,7 @@
     case CompilerFilter::kVerifyProfile:
     case CompilerFilter::kSpaceProfile:
     case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kLayoutProfile:
     case CompilerFilter::kEverythingProfile: return true;
   }
   UNREACHABLE();
@@ -121,6 +125,7 @@
       return CompilerFilter::kSpace;
 
     case CompilerFilter::kSpeedProfile:
+    case CompilerFilter::kLayoutProfile:
       return CompilerFilter::kSpeed;
 
     case CompilerFilter::kEverythingProfile:
@@ -146,6 +151,7 @@
     case CompilerFilter::kTime: return "time";
     case CompilerFilter::kSpeedProfile: return "speed-profile";
     case CompilerFilter::kSpeed: return "speed";
+    case CompilerFilter::kLayoutProfile: return "layout-profile";
     case CompilerFilter::kEverythingProfile: return "everything-profile";
     case CompilerFilter::kEverything: return "everything";
   }
@@ -173,6 +179,8 @@
     *filter = kSpeed;
   } else if (strcmp(option, "speed-profile") == 0) {
     *filter = kSpeedProfile;
+  } else if (strcmp(option, "layout-profile") == 0) {
+    *filter = kLayoutProfile;
   } else if (strcmp(option, "everything") == 0) {
     *filter = kEverything;
   } else if (strcmp(option, "everything-profile") == 0) {
diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h
index 37631cc..781d43a 100644
--- a/runtime/compiler_filter.h
+++ b/runtime/compiler_filter.h
@@ -39,6 +39,7 @@
     kSpace,               // Maximize space savings.
     kBalanced,            // Good performance return on compilation investment.
     kSpeedProfile,        // Maximize runtime performance based on profile.
+    kLayoutProfile,       // Temporary filter for dexlayout. Will be merged with kSpeedProfile.
     kSpeed,               // Maximize runtime performance.
     kEverythingProfile,   // Compile everything capable of being compiled based on profile.
     kEverything,          // Compile everything capable of being compiled.
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index d717ec0..b0c4597 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -136,7 +136,9 @@
       + "/core.art";
   }
 
-  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+  bool GetCachedImageFile(const std::string& image_location,
+                          /*out*/std::string* image,
+                          /*out*/std::string* error_msg) const {
     std::string cache;
     bool have_android_data;
     bool dalvik_cache_exists;
@@ -151,7 +153,14 @@
       *error_msg = "Failed to create dalvik cache";
       return false;
     }
-    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+    return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
+  }
+
+  // Returns the path to an image location whose contents differ from the
+  // image at GetImageLocation(). This is used for testing mismatched
+  // image checksums in the oat_file_assistant_tests.
+  std::string GetImageLocation2() const {
+    return GetImageDirectory() + "/core-npic.art";
   }
 
   std::string GetDexSrc1() const {
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
index 0febbed..f53ca4a 100644
--- a/runtime/dex_cache_resolved_classes.h
+++ b/runtime/dex_cache_resolved_classes.h
@@ -21,6 +21,8 @@
 #include <unordered_set>
 #include <vector>
 
+#include "dex_file_types.h"
+
 namespace art {
 
 // Data structure for passing around which classes belonging to a dex cache / dex file are resolved.
@@ -59,7 +61,7 @@
     return location_checksum_;
   }
 
-  const std::unordered_set<uint16_t>& GetClasses() const {
+  const std::unordered_set<dex::TypeIndex>& GetClasses() const {
     return classes_;
   }
 
@@ -68,7 +70,7 @@
   const std::string base_location_;
   const uint32_t location_checksum_;
   // Array of resolved class def indexes.
-  mutable std::unordered_set<uint16_t> classes_;
+  mutable std::unordered_set<dex::TypeIndex> classes_;
 };
 
 inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) {
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index 621b2c5..77a63c1 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -58,12 +58,12 @@
   return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
 }
 
-inline const char* DexFile::StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const {
+inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const {
   const TypeId& type_id = GetTypeId(idx);
   return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
 }
 
-inline const char* DexFile::StringByTypeIdx(uint32_t idx) const {
+inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const {
   const TypeId& type_id = GetTypeId(idx);
   return StringDataByIdx(type_id.descriptor_idx_);
 }
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 2ef7509..cc544fd 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -26,6 +26,7 @@
 
 #include <memory>
 #include <sstream>
+#include <type_traits>
 
 #include "base/enums.h"
 #include "base/file_magic.h"
@@ -44,6 +45,9 @@
 
 namespace art {
 
+static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
+static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial");
+
 static constexpr OatDexFile* kNoOatDexFile = nullptr;
 
 const char* DexFile::kClassesDex = "classes.dex";
@@ -550,7 +554,7 @@
   return atoi(version);
 }
 
-const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const {
+const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const {
   size_t num_class_defs = NumClassDefs();
   // Fast path for rare no class defs case.
   if (num_class_defs == 0) {
@@ -597,9 +601,9 @@
                                              const DexFile::StringId& name,
                                              const DexFile::TypeId& type) const {
   // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
-  const uint16_t class_idx = GetIndexForTypeId(declaring_klass);
+  const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
   const uint32_t name_idx = GetIndexForStringId(name);
-  const uint16_t type_idx = GetIndexForTypeId(type);
+  const dex::TypeIndex type_idx = GetIndexForTypeId(type);
   int32_t lo = 0;
   int32_t hi = NumFieldIds() - 1;
   while (hi >= lo) {
@@ -632,7 +636,7 @@
                                                const DexFile::StringId& name,
                                                const DexFile::ProtoId& signature) const {
   // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
-  const uint16_t class_idx = GetIndexForTypeId(declaring_klass);
+  const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
   const uint32_t name_idx = GetIndexForStringId(name);
   const uint16_t proto_idx = GetIndexForProtoId(signature);
   int32_t lo = 0;
@@ -687,7 +691,7 @@
   int32_t hi = NumTypeIds() - 1;
   while (hi >= lo) {
     int32_t mid = (hi + lo) / 2;
-    const TypeId& type_id = GetTypeId(mid);
+    const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
     const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_);
     const char* str = GetStringData(str_id);
     int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
@@ -726,7 +730,7 @@
   int32_t hi = NumTypeIds() - 1;
   while (hi >= lo) {
     int32_t mid = (hi + lo) / 2;
-    const TypeId& type_id = GetTypeId(mid);
+    const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
     if (string_idx > type_id.descriptor_idx_) {
       lo = mid + 1;
     } else if (string_idx < type_id.descriptor_idx_) {
@@ -738,20 +742,20 @@
   return nullptr;
 }
 
-const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx,
-                                             const uint16_t* signature_type_idxs,
+const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx,
+                                             const dex::TypeIndex* signature_type_idxs,
                                              uint32_t signature_length) const {
   int32_t lo = 0;
   int32_t hi = NumProtoIds() - 1;
   while (hi >= lo) {
     int32_t mid = (hi + lo) / 2;
     const DexFile::ProtoId& proto = GetProtoId(mid);
-    int compare = return_type_idx - proto.return_type_idx_;
+    int compare = return_type_idx.index_ - proto.return_type_idx_.index_;
     if (compare == 0) {
       DexFileParameterIterator it(*this, proto);
       size_t i = 0;
       while (it.HasNext() && i < signature_length && compare == 0) {
-        compare = signature_type_idxs[i] - it.GetTypeIdx();
+        compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_;
         it.Next();
         i++;
       }
@@ -775,8 +779,9 @@
 }
 
 // Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
-                             std::vector<uint16_t>* param_type_idxs) const {
+bool DexFile::CreateTypeList(const StringPiece& signature,
+                             dex::TypeIndex* return_type_idx,
+                             std::vector<dex::TypeIndex>* param_type_idxs) const {
   if (signature[0] != '(') {
     return false;
   }
@@ -813,7 +818,7 @@
     if (type_id == nullptr) {
       return false;
     }
-    uint16_t type_idx = GetIndexForTypeId(*type_id);
+    dex::TypeIndex type_idx = GetIndexForTypeId(*type_id);
     if (!process_return) {
       param_type_idxs->push_back(type_idx);
     } else {
@@ -825,8 +830,8 @@
 }
 
 const Signature DexFile::CreateSignature(const StringPiece& signature) const {
-  uint16_t return_type_idx;
-  std::vector<uint16_t> param_type_indices;
+  dex::TypeIndex return_type_idx;
+  std::vector<dex::TypeIndex> param_type_indices;
   bool success = CreateTypeList(signature, &return_type_idx, &param_type_indices);
   if (!success) {
     return Signature::NoSignature();
@@ -971,7 +976,8 @@
         }
 
         local_in_reg[reg].name_ = StringDataByIdx(name_idx);
-        local_in_reg[reg].descriptor_ = StringByTypeIdx(descriptor_idx);
+        local_in_reg[reg].descriptor_ =
+            StringByTypeIdx(dex::TypeIndex(dchecked_integral_cast<uint16_t>(descriptor_idx)));;
         local_in_reg[reg].signature_ = StringDataByIdx(signature_idx);
         local_in_reg[reg].start_address_ = address;
         local_in_reg[reg].reg_ = reg;
@@ -1225,9 +1231,9 @@
   return result;
 }
 
-std::string DexFile::PrettyType(uint32_t type_idx) const {
-  if (type_idx >= NumTypeIds()) {
-    return StringPrintf("<<invalid-type-idx-%d>>", type_idx);
+std::string DexFile::PrettyType(dex::TypeIndex type_idx) const {
+  if (type_idx.index_ >= NumTypeIds()) {
+    return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_);
   }
   const DexFile::TypeId& type_id = GetTypeId(type_idx);
   return PrettyDescriptor(GetTypeDescriptor(type_id));
@@ -1457,14 +1463,14 @@
 
 void CatchHandlerIterator::Next() {
   if (remaining_count_ > 0) {
-    handler_.type_idx_ = DecodeUnsignedLeb128(&current_data_);
+    handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(&current_data_));
     handler_.address_  = DecodeUnsignedLeb128(&current_data_);
     remaining_count_--;
     return;
   }
 
   if (catch_all_) {
-    handler_.type_idx_ = DexFile::kDexNoIndex16;
+    handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16);
     handler_.address_  = DecodeUnsignedLeb128(&current_data_);
     catch_all_ = false;
     return;
@@ -1474,4 +1480,13 @@
   remaining_count_ = -1;
 }
 
+namespace dex {
+
+std::ostream& operator<<(std::ostream& os, const TypeIndex& index) {
+  os << "TypeIndex[" << index.index_ << "]";
+  return os;
+}
+
+}  // namespace dex
+
 }  // namespace art
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index da9fa50..2384eb6 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -23,6 +23,7 @@
 
 #include "base/logging.h"
 #include "base/value_object.h"
+#include "dex_file_types.h"
 #include "globals.h"
 #include "invoke_type.h"
 #include "jni.h"
@@ -159,17 +160,28 @@
 
   // Raw field_id_item.
   struct FieldId {
-    uint16_t class_idx_;  // index into type_ids_ array for defining class
-    uint16_t type_idx_;  // index into type_ids_ array for field type
+    dex::TypeIndex class_idx_;  // index into type_ids_ array for defining class
+    dex::TypeIndex type_idx_;  // index into type_ids_ array for field type
     uint32_t name_idx_;  // index into string_ids_ array for field name
 
    private:
     DISALLOW_COPY_AND_ASSIGN(FieldId);
   };
 
+  // Raw proto_id_item.
+  struct ProtoId {
+    uint32_t shorty_idx_;        // index into string_ids array for shorty descriptor
+    dex::TypeIndex return_type_idx_;  // index into type_ids array for return type
+    uint16_t pad_;               // padding = 0
+    uint32_t parameters_off_;    // file offset to type_list for parameter types
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ProtoId);
+  };
+
   // Raw method_id_item.
   struct MethodId {
-    uint16_t class_idx_;  // index into type_ids_ array for defining class
+    dex::TypeIndex class_idx_;   // index into type_ids_ array for defining class
     uint16_t proto_idx_;  // index into proto_ids_ array for method prototype
     uint32_t name_idx_;  // index into string_ids_ array for method name
 
@@ -177,23 +189,12 @@
     DISALLOW_COPY_AND_ASSIGN(MethodId);
   };
 
-  // Raw proto_id_item.
-  struct ProtoId {
-    uint32_t shorty_idx_;  // index into string_ids array for shorty descriptor
-    uint16_t return_type_idx_;  // index into type_ids array for return type
-    uint16_t pad_;             // padding = 0
-    uint32_t parameters_off_;  // file offset to type_list for parameter types
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(ProtoId);
-  };
-
   // Raw class_def_item.
   struct ClassDef {
-    uint16_t class_idx_;  // index into type_ids_ array for this class
+    dex::TypeIndex class_idx_;  // index into type_ids_ array for this class
     uint16_t pad1_;  // padding = 0
     uint32_t access_flags_;
-    uint16_t superclass_idx_;  // index into type_ids_ array for superclass
+    dex::TypeIndex superclass_idx_;  // index into type_ids_ array for superclass
     uint16_t pad2_;  // padding = 0
     uint32_t interfaces_off_;  // file offset to TypeList
     uint32_t source_file_idx_;  // index into string_ids_ for source file name
@@ -225,7 +226,7 @@
 
   // Raw type_item.
   struct TypeItem {
-    uint16_t type_idx_;  // index into type_ids section
+    dex::TypeIndex type_idx_;  // index into type_ids section
 
    private:
     DISALLOW_COPY_AND_ASSIGN(TypeItem);
@@ -540,23 +541,23 @@
   }
 
   // Returns the TypeId at the specified index.
-  const TypeId& GetTypeId(uint32_t idx) const {
-    DCHECK_LT(idx, NumTypeIds()) << GetLocation();
-    return type_ids_[idx];
+  const TypeId& GetTypeId(dex::TypeIndex idx) const {
+    DCHECK_LT(idx.index_, NumTypeIds()) << GetLocation();
+    return type_ids_[idx.index_];
   }
 
-  uint16_t GetIndexForTypeId(const TypeId& type_id) const {
+  dex::TypeIndex GetIndexForTypeId(const TypeId& type_id) const {
     CHECK_GE(&type_id, type_ids_) << GetLocation();
     CHECK_LT(&type_id, type_ids_ + header_->type_ids_size_) << GetLocation();
     size_t result = &type_id - type_ids_;
     DCHECK_LT(result, 65536U) << GetLocation();
-    return static_cast<uint16_t>(result);
+    return dex::TypeIndex(static_cast<uint16_t>(result));
   }
 
   // Get the descriptor string associated with a given type index.
-  const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const;
+  const char* StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const;
 
-  const char* StringByTypeIdx(uint32_t idx) const;
+  const char* StringByTypeIdx(dex::TypeIndex idx) const;
 
   // Returns the type descriptor string of a type id.
   const char* GetTypeDescriptor(const TypeId& type_id) const;
@@ -671,7 +672,7 @@
   const char* GetClassDescriptor(const ClassDef& class_def) const;
 
   // Looks up a class definition by its type index.
-  const ClassDef* FindClassDef(uint16_t type_idx) const;
+  const ClassDef* FindClassDef(dex::TypeIndex type_idx) const;
 
   const TypeList* GetInterfacesList(const ClassDef& class_def) const {
     if (class_def.interfaces_off_ == 0) {
@@ -711,7 +712,7 @@
   }
 
   // Returns the ProtoId at the specified index.
-  const ProtoId& GetProtoId(uint32_t idx) const {
+  const ProtoId& GetProtoId(uint16_t idx) const {
     DCHECK_LT(idx, NumProtoIds()) << GetLocation();
     return proto_ids_[idx];
   }
@@ -723,16 +724,18 @@
   }
 
   // Looks up a proto id for a given return type and signature type list
-  const ProtoId* FindProtoId(uint16_t return_type_idx,
-                             const uint16_t* signature_type_idxs, uint32_t signature_length) const;
-  const ProtoId* FindProtoId(uint16_t return_type_idx,
-                             const std::vector<uint16_t>& signature_type_idxs) const {
+  const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
+                             const dex::TypeIndex* signature_type_idxs,
+                             uint32_t signature_length) const;
+  const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
+                             const std::vector<dex::TypeIndex>& signature_type_idxs) const {
     return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size());
   }
 
   // Given a signature place the type ids into the given vector, returns true on success
-  bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
-                      std::vector<uint16_t>* param_type_idxs) const;
+  bool CreateTypeList(const StringPiece& signature,
+                      dex::TypeIndex* return_type_idx,
+                      std::vector<dex::TypeIndex>* param_type_idxs) const;
 
   // Create a Signature from the given string signature or return Signature::NoSignature if not
   // possible.
@@ -1010,6 +1013,11 @@
     return oat_dex_file_;
   }
 
+  // Used by oat writer.
+  void SetOatDexFile(OatDexFile* oat_dex_file) const {
+    oat_dex_file_ = oat_dex_file;
+  }
+
   // Utility methods for reading integral values from a buffer.
   static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth);
   static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right);
@@ -1021,7 +1029,7 @@
   // Returns a human-readable form of the field at an index.
   std::string PrettyField(uint32_t field_idx, bool with_type = true) const;
   // Returns a human-readable form of the type at an index.
-  std::string PrettyType(uint32_t type_idx) const;
+  std::string PrettyType(dex::TypeIndex type_idx) const;
 
  private:
   static std::unique_ptr<const DexFile> OpenFile(int fd,
@@ -1138,9 +1146,10 @@
   // If this dex file was loaded from an oat file, oat_dex_file_ contains a
   // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
   // null.
-  const OatDexFile* oat_dex_file_;
+  mutable const OatDexFile* oat_dex_file_;
 
   friend class DexFileVerifierTest;
+  friend class OatWriter;
   ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName);  // for constructor
 };
 
@@ -1165,11 +1174,11 @@
   bool HasNext() const { return pos_ < size_; }
   size_t Size() const { return size_; }
   void Next() { ++pos_; }
-  uint16_t GetTypeIdx() {
+  dex::TypeIndex GetTypeIdx() {
     return type_list_->GetTypeItem(pos_).type_idx_;
   }
   const char* GetDescriptor() {
-    return dex_file_.StringByTypeIdx(GetTypeIdx());
+    return dex_file_.StringByTypeIdx(dex::TypeIndex(GetTypeIdx()));
   }
  private:
   const DexFile& dex_file_;
@@ -1455,7 +1464,7 @@
       Init(handler_data);
     }
 
-    uint16_t GetHandlerTypeIndex() const {
+    dex::TypeIndex GetHandlerTypeIndex() const {
       return handler_.type_idx_;
     }
     uint32_t GetHandlerAddress() const {
@@ -1476,7 +1485,7 @@
     void Init(const uint8_t* handler_data);
 
     struct CatchHandlerItem {
-      uint16_t type_idx_;  // type index of the caught exception type
+      dex::TypeIndex type_idx_;  // type index of the caught exception type
       uint32_t address_;  // handler address
     } handler_;
     const uint8_t* current_data_;  // the current handler in dex file.
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 835f456..3fe2c40 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -90,7 +90,7 @@
     const uint8_t* annotation = annotation_item->annotation_;
     uint32_t type_index = DecodeUnsignedLeb128(&annotation);
 
-    if (strcmp(descriptor, dex_file.StringByTypeIdx(type_index)) == 0) {
+    if (strcmp(descriptor, dex_file.StringByTypeIdx(dex::TypeIndex(type_index))) == 0) {
       result = annotation_item;
       break;
     }
@@ -246,7 +246,7 @@
   StackHandleScope<2> hs(self);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::Class> annotation_class(hs.NewHandle(
-      class_linker->ResolveType(klass->GetDexFile(), type_index, klass.Get())));
+      class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get())));
   if (annotation_class.Get() == nullptr) {
     LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index;
     DCHECK(Thread::Current()->IsExceptionPending());
@@ -370,13 +370,14 @@
       if (result_style == DexFile::kAllRaw) {
         annotation_value->value_.SetI(index);
       } else {
+        dex::TypeIndex type_index(index);
         element_object = Runtime::Current()->GetClassLinker()->ResolveType(
-            klass->GetDexFile(), index, klass.Get());
+            klass->GetDexFile(), type_index, klass.Get());
         set_object = true;
         if (element_object == nullptr) {
           CHECK(self->IsExceptionPending());
           if (result_style == DexFile::kAllObjects) {
-            const char* msg = dex_file.StringByTypeIdx(index);
+            const char* msg = dex_file.StringByTypeIdx(type_index);
             self->ThrowNewWrappedException("Ljava/lang/TypeNotPresentException;", msg);
             element_object = self->GetException();
             self->ClearException();
@@ -665,7 +666,7 @@
     const uint8_t* annotation = annotation_item->annotation_;
     uint32_t type_index = DecodeUnsignedLeb128(&annotation);
     mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
-        klass->GetDexFile(), type_index, klass.Get());
+        klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get());
     if (resolved_class == nullptr) {
       std::string temp;
       LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
@@ -1345,7 +1346,9 @@
       break;
     }
     case kType: {
-      mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_,
+      mirror::Class* resolved = linker_->ResolveType(dex_file_,
+                                                     dex::TypeIndex(jval_.i),
+                                                     *dex_cache_,
                                                      *class_loader_);
       field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved);
       break;
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 8e1501f..f94d07b 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -415,14 +415,14 @@
 
 TEST_F(DexFileTest, FindTypeId) {
   for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) {
-    const char* type_str = java_lang_dex_file_->StringByTypeIdx(i);
+    const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i));
     const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str);
     ASSERT_TRUE(type_str_id != nullptr);
     uint32_t type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id);
     const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx);
     ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str));
     ASSERT_TRUE(type_id != nullptr);
-    EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id), i);
+    EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i);
   }
 }
 
@@ -430,7 +430,7 @@
   for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) {
     const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i);
     const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find);
-    std::vector<uint16_t> to_find_types;
+    std::vector<dex::TypeIndex> to_find_types;
     if (to_find_tl != nullptr) {
       for (size_t j = 0; j < to_find_tl->Size(); j++) {
         to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_);
diff --git a/runtime/dex_file_types.h b/runtime/dex_file_types.h
new file mode 100644
index 0000000..c6d95a1
--- /dev/null
+++ b/runtime/dex_file_types.h
@@ -0,0 +1,74 @@
+/*
+ * 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_DEX_FILE_TYPES_H_
+#define ART_RUNTIME_DEX_FILE_TYPES_H_
+
+#include <limits>
+#include <ostream>
+
+namespace art {
+namespace dex {
+
+class TypeIndex {
+ public:
+  uint16_t index_;
+
+  TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
+  explicit TypeIndex(uint16_t idx) : index_(idx) {}
+
+  bool IsValid() const {
+    return index_ != std::numeric_limits<decltype(index_)>::max();
+  }
+  static TypeIndex Invalid() {
+    return TypeIndex(std::numeric_limits<decltype(index_)>::max());
+  }
+
+  bool operator==(const TypeIndex& other) const {
+    return index_ == other.index_;
+  }
+  bool operator!=(const TypeIndex& other) const {
+    return index_ != other.index_;
+  }
+  bool operator<(const TypeIndex& other) const {
+    return index_ < other.index_;
+  }
+  bool operator<=(const TypeIndex& other) const {
+    return index_ <= other.index_;
+  }
+  bool operator>(const TypeIndex& other) const {
+    return index_ > other.index_;
+  }
+  bool operator>=(const TypeIndex& other) const {
+    return index_ >= other.index_;
+  }
+};
+std::ostream& operator<<(std::ostream& os, const TypeIndex& index);
+
+}  // namespace dex
+}  // namespace art
+
+namespace std {
+
+template<> struct hash<art::dex::TypeIndex> {
+  size_t operator()(const art::dex::TypeIndex& index) const {
+    return hash<uint16_t>()(index.index_);
+  }
+};
+
+}  // namespace std
+
+#endif  // ART_RUNTIME_DEX_FILE_TYPES_H_
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index be25803..ed50711 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <zlib.h>
 
+#include <limits>
 #include <memory>
 
 #include "base/stringprintf.h"
@@ -31,6 +32,16 @@
 
 namespace art {
 
+static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
+
+static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
+  return (high == 0) || ((high == 0xffffU) && (low == 0xffffU));
+}
+
+static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) {
+  return (high == 0);
+}
+
 static uint32_t MapTypeToBitMask(uint32_t map_type) {
   switch (map_type) {
     case DexFile::kDexTypeHeaderItem:               return 1 << 0;
@@ -76,8 +87,9 @@
   return dex_file_->StringDataByIdx(idx);
 }
 
-const char* DexFileVerifier::CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_string) {
-  if (UNLIKELY(!CheckIndex(type_idx, dex_file_->NumTypeIds(), error_string))) {
+const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx,
+                                                      const char* error_string) {
+  if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) {
     return nullptr;
   }
   const DexFile::TypeId& type_id = dex_file_->GetTypeId(type_idx);
@@ -525,7 +537,7 @@
 bool DexFileVerifier::CheckClassDataItemField(uint32_t idx,
                                               uint32_t access_flags,
                                               uint32_t class_access_flags,
-                                              uint16_t class_type_index,
+                                              dex::TypeIndex class_type_index,
                                               bool expect_static) {
   // Check for overflow.
   if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
@@ -533,13 +545,13 @@
   }
 
   // Check that it's the right class.
-  uint16_t my_class_index =
+  dex::TypeIndex my_class_index =
       (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)->
           class_idx_;
   if (class_type_index != my_class_index) {
     ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16,
-                      my_class_index,
-                      class_type_index);
+                      my_class_index.index_,
+                      class_type_index.index_);
     return false;
   }
 
@@ -563,7 +575,7 @@
 bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
                                                uint32_t access_flags,
                                                uint32_t class_access_flags,
-                                               uint16_t class_type_index,
+                                               dex::TypeIndex class_type_index,
                                                uint32_t code_offset,
                                                std::unordered_set<uint32_t>* direct_method_indexes,
                                                bool expect_direct) {
@@ -574,13 +586,13 @@
   }
 
   // Check that it's the right class.
-  uint16_t my_class_index =
+  dex::TypeIndex my_class_index =
       (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
           class_idx_;
   if (class_type_index != my_class_index) {
-    ErrorStringPrintf("Method's class index unexpected, %" PRIu16 "vs %" PRIu16,
-                      my_class_index,
-                      class_type_index);
+    ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16,
+                      my_class_index.index_,
+                      class_type_index.index_);
     return false;
   }
 
@@ -789,7 +801,7 @@
 
 bool DexFileVerifier::FindClassFlags(uint32_t index,
                                      bool is_field,
-                                     uint16_t* class_type_index,
+                                     dex::TypeIndex* class_type_index,
                                      uint32_t* class_access_flags) {
   DCHECK(class_type_index != nullptr);
   DCHECK(class_access_flags != nullptr);
@@ -811,7 +823,7 @@
   }
 
   // Check if that is valid.
-  if (*class_type_index >= header_->type_ids_size_) {
+  if (class_type_index->index_ >= header_->type_ids_size_) {
     return false;
   }
 
@@ -836,7 +848,7 @@
                                                  uint32_t curr_index,
                                                  uint32_t prev_index,
                                                  bool* have_class,
-                                                 uint16_t* class_type_index,
+                                                 dex::TypeIndex* class_type_index,
                                                  uint32_t* class_access_flags) {
   if (curr_index < prev_index) {
     ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
@@ -862,7 +874,7 @@
 template <bool kStatic>
 bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it,
                                                     bool* have_class,
-                                                    uint16_t* class_type_index,
+                                                    dex::TypeIndex* class_type_index,
                                                     uint32_t* class_access_flags) {
   DCHECK(it != nullptr);
   // These calls use the raw access flags to check whether the whole dex field is valid.
@@ -897,7 +909,7 @@
     ClassDataItemIterator* it,
     std::unordered_set<uint32_t>* direct_method_indexes,
     bool* have_class,
-    uint16_t* class_type_index,
+    dex::TypeIndex* class_type_index,
     uint32_t* class_access_flags) {
   uint32_t prev_index = 0;
   for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
@@ -935,7 +947,7 @@
   // So we need to explicitly search with the first item we find (either field or method), and then,
   // as the lookup is expensive, cache the result.
   bool have_class = false;
-  uint16_t class_type_index;
+  dex::TypeIndex class_type_index;
   uint32_t class_access_flags;
 
   // Check fields.
@@ -1682,26 +1694,27 @@
   return true;
 }
 
-uint16_t DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) {
+dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) {
   ClassDataItemIterator it(*dex_file_, ptr);
   *success = true;
 
   if (it.HasNextStaticField() || it.HasNextInstanceField()) {
     LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id",
-               *success = false; return DexFile::kDexNoIndex16)
+               *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
     return field->class_idx_;
   }
 
   if (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
     LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id",
-                *success = false; return DexFile::kDexNoIndex16)
+                *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
     return method->class_idx_;
   }
 
-  return DexFile::kDexNoIndex16;
+  return dex::TypeIndex(DexFile::kDexNoIndex16);
 }
 
-uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success) {
+dex::TypeIndex DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr,
+                                                                     bool* success) {
   const DexFile::AnnotationsDirectoryItem* item =
       reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr);
   *success = true;
@@ -1709,25 +1722,25 @@
   if (item->fields_size_ != 0) {
     DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1);
     LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id",
-               *success = false; return DexFile::kDexNoIndex16)
+               *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
     return field->class_idx_;
   }
 
   if (item->methods_size_ != 0) {
     DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1);
     LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id",
-                *success = false; return DexFile::kDexNoIndex16)
+                *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
     return method->class_idx_;
   }
 
   if (item->parameters_size_ != 0) {
     DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1);
     LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id",
-                *success = false; return DexFile::kDexNoIndex16)
+                *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
     return method->class_idx_;
   }
 
-  return DexFile::kDexNoIndex16;
+  return dex::TypeIndex(DexFile::kDexNoIndex16);
 }
 
 bool DexFileVerifier::CheckInterStringIdItem() {
@@ -1788,6 +1801,12 @@
     return false;
   }
 
+  // Check that return type is representable as a uint16_t;
+  if (UNLIKELY(!IsValidOrNoTypeId(item->return_type_idx_.index_, item->pad_))) {
+    ErrorStringPrintf("proto with return type idx outside uint16_t range '%x:%x'",
+                      item->pad_, item->return_type_idx_.index_);
+    return false;
+  }
   // Check the return type and advance the shorty.
   LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx")
   if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) {
@@ -1797,7 +1816,8 @@
 
   DexFileParameterIterator it(*dex_file_, *item);
   while (it.HasNext() && *shorty != '\0') {
-    if (!CheckIndex(it.GetTypeIdx(), dex_file_->NumTypeIds(),
+    if (!CheckIndex(it.GetTypeIdx().index_,
+                    dex_file_->NumTypeIds(),
                     "inter_proto_id_item shorty type_idx")) {
       return false;
     }
@@ -1824,10 +1844,10 @@
       DexFileParameterIterator prev_it(*dex_file_, *prev);
 
       while (curr_it.HasNext() && prev_it.HasNext()) {
-        uint16_t prev_idx = prev_it.GetTypeIdx();
-        uint16_t curr_idx = curr_it.GetTypeIdx();
-        DCHECK_NE(prev_idx, DexFile::kDexNoIndex16);
-        DCHECK_NE(curr_idx, DexFile::kDexNoIndex16);
+        dex::TypeIndex prev_idx = prev_it.GetTypeIdx();
+        dex::TypeIndex curr_idx = curr_it.GetTypeIdx();
+        DCHECK_NE(prev_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
+        DCHECK_NE(curr_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
 
         if (prev_idx < curr_idx) {
           break;
@@ -1949,9 +1969,21 @@
 bool DexFileVerifier::CheckInterClassDefItem() {
   const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_);
 
+  // Check that class_idx_ is representable as a uint16_t;
+  if (UNLIKELY(!IsValidTypeId(item->class_idx_.index_, item->pad1_))) {
+    ErrorStringPrintf("class with type idx outside uint16_t range '%x:%x'", item->pad1_,
+                      item->class_idx_.index_);
+    return false;
+  }
+  // Check that superclass_idx_ is representable as a uint16_t;
+  if (UNLIKELY(!IsValidOrNoTypeId(item->superclass_idx_.index_, item->pad2_))) {
+    ErrorStringPrintf("class with superclass type idx outside uint16_t range '%x:%x'", item->pad2_,
+                      item->superclass_idx_.index_);
+    return false;
+  }
   // Check for duplicate class def.
   if (defined_classes_.find(item->class_idx_) != defined_classes_.end()) {
-    ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_);
+    ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_.index_);
     return false;
   }
   defined_classes_.insert(item->class_idx_);
@@ -1985,12 +2017,13 @@
     return false;
   }
 
-  if (item->superclass_idx_ != DexFile::kDexNoIndex16) {
+  if (item->superclass_idx_.IsValid()) {
     if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) {
       // Check that a class does not inherit from itself directly (by having
       // the same type idx as its super class).
       if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) {
-        ErrorStringPrintf("Class with same type idx as its superclass: '%d'", item->class_idx_);
+        ErrorStringPrintf("Class with same type idx as its superclass: '%d'",
+                          item->class_idx_.index_);
         return false;
       }
 
@@ -2004,8 +2037,8 @@
           ErrorStringPrintf("Invalid class definition ordering:"
                             " class with type idx: '%d' defined before"
                             " superclass with type idx: '%d'",
-                            item->class_idx_,
-                            item->superclass_idx_);
+                            item->class_idx_.index_,
+                            item->superclass_idx_.index_);
           return false;
         }
       }
@@ -2029,7 +2062,7 @@
         // same type idx as one of its immediate implemented interfaces).
         if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) {
           ErrorStringPrintf("Class with same type idx as implemented interface: '%d'",
-                            item->class_idx_);
+                            item->class_idx_.index_);
           return false;
         }
 
@@ -2044,8 +2077,8 @@
             ErrorStringPrintf("Invalid class definition ordering:"
                               " class with type idx: '%d' defined before"
                               " implemented interface with type idx: '%d'",
-                              item->class_idx_,
-                              interfaces->GetTypeItem(i).type_idx_);
+                              item->class_idx_.index_,
+                              interfaces->GetTypeItem(i).type_idx_.index_);
             return false;
           }
         }
@@ -2065,9 +2098,9 @@
      * practice the number of interfaces implemented by any given class is low.
      */
     for (uint32_t i = 1; i < size; i++) {
-      uint32_t idx1 = interfaces->GetTypeItem(i).type_idx_;
+      dex::TypeIndex idx1 = interfaces->GetTypeItem(i).type_idx_;
       for (uint32_t j =0; j < i; j++) {
-        uint32_t idx2 = interfaces->GetTypeItem(j).type_idx_;
+        dex::TypeIndex idx2 = interfaces->GetTypeItem(j).type_idx_;
         if (UNLIKELY(idx1 == idx2)) {
           ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1));
           return false;
@@ -2080,11 +2113,12 @@
   if (item->class_data_off_ != 0) {
     const uint8_t* data = begin_ + item->class_data_off_;
     bool success;
-    uint16_t data_definer = FindFirstClassDataDefiner(data, &success);
+    dex::TypeIndex data_definer = FindFirstClassDataDefiner(data, &success);
     if (!success) {
       return false;
     }
-    if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) {
+    if (UNLIKELY((data_definer != item->class_idx_) &&
+                 (data_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
       ErrorStringPrintf("Invalid class_data_item");
       return false;
     }
@@ -2099,12 +2133,12 @@
     }
     const uint8_t* data = begin_ + item->annotations_off_;
     bool success;
-    uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
+    dex::TypeIndex annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
     if (!success) {
       return false;
     }
     if (UNLIKELY((annotations_definer != item->class_idx_) &&
-                 (annotations_definer != DexFile::kDexNoIndex16))) {
+                 (annotations_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
       ErrorStringPrintf("Invalid annotations_directory_item");
       return false;
     }
@@ -2165,7 +2199,7 @@
 bool DexFileVerifier::CheckInterClassDataItem() {
   ClassDataItemIterator it(*dex_file_, ptr_);
   bool success;
-  uint16_t defining_class = FindFirstClassDataDefiner(ptr_, &success);
+  dex::TypeIndex defining_class = FindFirstClassDataDefiner(ptr_, &success);
   if (!success) {
     return false;
   }
@@ -2197,7 +2231,7 @@
   const DexFile::AnnotationsDirectoryItem* item =
       reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
   bool success;
-  uint16_t defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success);
+  dex::TypeIndex defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success);
   if (!success) {
     return false;
   }
@@ -2315,6 +2349,14 @@
         break;
       }
       case DexFile::kDexTypeClassDefItem: {
+        // There shouldn't be more class definitions than type ids allow.
+        // This check should be redundant, since there are checks that the
+        // class_idx_ is within range and that there is only one definition
+        // for a given type id.
+        if (i > kTypeIdLimit) {
+          ErrorStringPrintf("Too many class definition items");
+          return false;
+        }
         if (!CheckInterClassDefItem()) {
           return false;
         }
@@ -2333,6 +2375,14 @@
         break;
       }
       case DexFile::kDexTypeClassDataItem: {
+        // There shouldn't be more class data than type ids allow.
+        // This check should be redundant, since there are checks that the
+        // class_idx_ is within range and that there is only one definition
+        // for a given type id.
+        if (i > kTypeIdLimit) {
+          ErrorStringPrintf("Too many class data items");
+          return false;
+        }
         if (!CheckInterClassDataItem()) {
           return false;
         }
@@ -2471,15 +2521,15 @@
 
 static std::string GetClassOrError(const uint8_t* const begin,
                                    const DexFile::Header* const header,
-                                   uint32_t class_idx) {
+                                   dex::TypeIndex class_idx) {
   // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and
   // it has already been checked in `DexFileVerifier::CheckClassDataItemField()`
   // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match
   // a valid defining class.
-  CHECK_LT(class_idx, header->type_ids_size_);
+  CHECK_LT(class_idx.index_, header->type_ids_size_);
 
   const DexFile::TypeId* type_id =
-      reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx;
+      reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx.index_;
 
   // Assume that the data is OK at this point. Type id offsets have been checked at this point.
 
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 133e432..19a89de 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -20,6 +20,7 @@
 #include <unordered_set>
 
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "safe_map.h"
 
 namespace art {
@@ -76,12 +77,12 @@
   bool CheckClassDataItemField(uint32_t idx,
                                uint32_t access_flags,
                                uint32_t class_access_flags,
-                               uint16_t class_type_index,
+                               dex::TypeIndex class_type_index,
                                bool expect_static);
   bool CheckClassDataItemMethod(uint32_t idx,
                                 uint32_t access_flags,
                                 uint32_t class_access_flags,
-                                uint16_t class_type_index,
+                                dex::TypeIndex class_type_index,
                                 uint32_t code_offset,
                                 std::unordered_set<uint32_t>* direct_method_indexes,
                                 bool expect_direct);
@@ -90,7 +91,7 @@
                                   uint32_t curr_index,
                                   uint32_t prev_index,
                                   bool* have_class,
-                                  uint16_t* class_type_index,
+                                  dex::TypeIndex* class_type_index,
                                   uint32_t* class_access_flags);
 
   bool CheckPadding(size_t offset, uint32_t aligned_offset);
@@ -104,7 +105,7 @@
   template <bool kStatic>
   bool CheckIntraClassDataItemFields(ClassDataItemIterator* it,
                                      bool* have_class,
-                                     uint16_t* class_type_index,
+                                     dex::TypeIndex* class_type_index,
                                      uint32_t* class_access_flags);
   // Check all methods of the given type from the given iterator. Load the class data from the first
   // method, if necessary (and return it), or use the given values.
@@ -112,7 +113,7 @@
   bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
                                       std::unordered_set<uint32_t>* direct_method_indexes,
                                       bool* have_class,
-                                      uint16_t* class_type_index,
+                                      dex::TypeIndex* class_type_index,
                                       uint32_t* class_access_flags);
 
   bool CheckIntraCodeItem();
@@ -130,8 +131,8 @@
 
   // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an
   // additional out parameter to signal any errors loading an index.
-  uint16_t FindFirstClassDataDefiner(const uint8_t* ptr, bool* success);
-  uint16_t FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success);
+  dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success);
+  dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success);
 
   bool CheckInterStringIdItem();
   bool CheckInterTypeIdItem();
@@ -150,7 +151,7 @@
   // Load a string by (type) index. Checks whether the index is in bounds, printing the error if
   // not. If there is an error, null is returned.
   const char* CheckLoadStringByIdx(uint32_t idx, const char* error_fmt);
-  const char* CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_fmt);
+  const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt);
 
   // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if
   // not. If there is an error, null is returned.
@@ -168,7 +169,7 @@
   // linear search. The output values should thus be cached by the caller.
   bool FindClassFlags(uint32_t index,
                       bool is_field,
-                      uint16_t* class_type_index,
+                      dex::TypeIndex* class_type_index,
                       uint32_t* class_access_flags);
 
   // Check validity of the given access flags, interpreted for a field in the context of a class
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 3801c22..0e0929f 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -26,6 +26,7 @@
 #include "base/macros.h"
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
+#include "dex_file_types.h"
 #include "leb128.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
@@ -155,7 +156,7 @@
       "method_id_class_idx",
       [](DexFile* dex_file) {
         DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
-        method_id->class_idx_ = 0xFF;
+        method_id->class_idx_ = dex::TypeIndex(0xFF);
       },
       "could not find declaring class for direct method index 0");
 
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index c766b54..751bd51 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -208,9 +208,9 @@
         case CONST_CLASS:
         case NEW_INSTANCE:
           if (file != nullptr) {
-            uint32_t type_idx = VRegB_21c();
-            os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyType(type_idx)
-               << " // type@" << type_idx;
+            dex::TypeIndex type_idx(VRegB_21c());
+            os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", "
+               << file->PrettyType(type_idx) << " // type@" << type_idx;
             break;
           }
           FALLTHROUGH_INTENDED;
@@ -302,17 +302,19 @@
           FALLTHROUGH_INTENDED;
         case INSTANCE_OF:
           if (file != nullptr) {
-            uint32_t type_idx = VRegC_22c();
-            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
-               << file->PrettyType(type_idx) << " // type@" << type_idx;
+            dex::TypeIndex type_idx(VRegC_22c());
+            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
+               << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
+               << " // type@" << type_idx.index_;
             break;
           }
           FALLTHROUGH_INTENDED;
         case NEW_ARRAY:
           if (file != nullptr) {
-            uint32_t type_idx = VRegC_22c();
-            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
-               << file->PrettyType(type_idx) << " // type@" << type_idx;
+            dex::TypeIndex type_idx(VRegC_22c());
+            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
+               << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
+               << " // type@" << type_idx.index_;
             break;
           }
           FALLTHROUGH_INTENDED;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 8eb1a79..99b9f9d 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -480,6 +480,18 @@
     insns[1] = val;
   }
 
+  void SetVRegA_21c(uint8_t val) {
+    DCHECK(FormatOf(Opcode()) == k21c);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[0] = (val << 8) | (insns[0] & 0x00ff);
+  }
+
+  void SetVRegB_21c(uint16_t val) {
+    DCHECK(FormatOf(Opcode()) == k21c);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[1] = val;
+  }
+
   // Returns the format of the given opcode.
   static Format FormatOf(Code opcode) {
     return kInstructionFormats[opcode];
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ed60f59..ac52f4e 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -129,7 +129,7 @@
 
 template <const bool kAccessCheck>
 ALWAYS_INLINE
-inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
+inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx,
                                        ArtMethod* method,
                                        Thread* self,
                                        bool* slow_path) {
@@ -219,7 +219,7 @@
 // check.
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE
-inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
+inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx,
                                            ArtMethod* method,
                                            Thread* self,
                                            gc::AllocatorType allocator_type) {
@@ -275,7 +275,7 @@
 
 template <bool kAccessCheck>
 ALWAYS_INLINE
-inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
+inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx,
                                       int32_t component_count,
                                       ArtMethod* method,
                                       bool* slow_path) {
@@ -313,7 +313,7 @@
 // check.
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE
-inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
+inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx,
                                          int32_t component_count,
                                          ArtMethod* method,
                                          Thread* self,
@@ -562,7 +562,7 @@
       StackHandleScope<2> hs2(self);
       HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object));
       Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass()));
-      const uint16_t method_type_idx =
+      const dex::TypeIndex method_type_idx =
           h_referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
       mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer);
       if (UNLIKELY(method_reference_class == nullptr)) {
@@ -758,7 +758,8 @@
     return resolved_method;
   } else if (type == kSuper) {
     // TODO This lookup is rather slow.
-    uint16_t method_type_idx = referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
+    dex::TypeIndex method_type_idx =
+        referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
     mirror::Class* method_reference_class =
         referring_class->GetDexCache()->GetResolvedType(method_type_idx);
     if (method_reference_class == nullptr) {
@@ -788,8 +789,11 @@
   }
 }
 
-inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, ArtMethod* referrer, Thread* self,
-                                             bool can_run_clinit, bool verify_access) {
+inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx,
+                                             ArtMethod* referrer,
+                                             Thread* self,
+                                             bool can_run_clinit,
+                                             bool verify_access) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   mirror::Class* klass = class_linker->ResolveType(type_idx, referrer);
   if (UNLIKELY(klass == nullptr)) {
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 1ccb4b0..5390165 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -38,7 +38,7 @@
 
 namespace art {
 
-static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx,
+static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx,
                                                       int32_t component_count,
                                                       ArtMethod* referrer,
                                                       Thread* self,
@@ -82,10 +82,12 @@
 }
 
 // Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
-                                          ArtMethod* referrer, Thread* self,
+mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
+                                          int32_t component_count,
+                                          ArtMethod* referrer,
+                                          Thread* self,
                                           bool access_check,
-                                          gc::AllocatorType /* allocator_type */) {
+                                          gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
   mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
                                                   access_check);
   if (UNLIKELY(klass == nullptr)) {
@@ -101,12 +103,13 @@
 }
 
 // Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
-                                                      int32_t component_count,
-                                                      ArtMethod* referrer,
-                                                      Thread* self,
-                                                      bool access_check,
-                                                      gc::AllocatorType /* allocator_type */) {
+mirror::Array* CheckAndAllocArrayFromCodeInstrumented(
+    dex::TypeIndex type_idx,
+    int32_t component_count,
+    ArtMethod* referrer,
+    Thread* self,
+    bool access_check,
+    gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
   mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
                                                   access_check);
   if (UNLIKELY(klass == nullptr)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index bcddfb0..d87dc67 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "dex_instruction.h"
+#include "dex_file_types.h"
 #include "gc/allocator_type.h"
 #include "handle.h"
 #include "invoke_type.h"
@@ -45,7 +46,7 @@
 class Thread;
 
 template <const bool kAccessCheck>
-ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
+ALWAYS_INLINE inline mirror::Class* CheckObjectAlloc(dex::TypeIndex type_idx,
                                                      ArtMethod* method,
                                                      Thread* self,
                                                      bool* slow_path)
@@ -63,7 +64,7 @@
 // When verification/compiler hasn't been able to verify access, optionally perform an access
 // check.
 template <bool kAccessCheck, bool kInstrumented>
-ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
+ALWAYS_INLINE inline mirror::Object* AllocObjectFromCode(dex::TypeIndex type_idx,
                                                          ArtMethod* method,
                                                          Thread* self,
                                                          gc::AllocatorType allocator_type)
@@ -89,7 +90,7 @@
 
 
 template <bool kAccessCheck>
-ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
+ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx,
                                                     int32_t component_count,
                                                     ArtMethod* method,
                                                     bool* slow_path)
@@ -101,7 +102,7 @@
 // When verification/compiler hasn't been able to verify access, optionally perform an access
 // check.
 template <bool kAccessCheck, bool kInstrumented>
-ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
+ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx,
                                                        int32_t component_count,
                                                        ArtMethod* method,
                                                        Thread* self,
@@ -118,19 +119,21 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
-                                                 ArtMethod* method, Thread* self,
-                                                 bool access_check,
-                                                 gc::AllocatorType allocator_type)
+mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
+                                          int32_t component_count,
+                                          ArtMethod* method,
+                                          Thread* self,
+                                          bool access_check,
+                                          gc::AllocatorType allocator_type)
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
-                                                             int32_t component_count,
-                                                             ArtMethod* method,
-                                                             Thread* self,
-                                                             bool access_check,
-                                                             gc::AllocatorType allocator_type)
+mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx,
+                                                      int32_t component_count,
+                                                      ArtMethod* method,
+                                                      Thread* self,
+                                                      bool access_check,
+                                                      gc::AllocatorType allocator_type)
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
@@ -177,7 +180,7 @@
                                  InvokeType type)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
+inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx,
                                              ArtMethod* referrer,
                                              Thread* self,
                                              bool can_run_clinit,
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 4a7e819..d2fee9a 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -19,6 +19,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "callee_save_frame.h"
+#include "dex_file_types.h"
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object_array-inl.h"
@@ -34,7 +35,8 @@
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (kUseTlabFastPath && !(instrumented_bool) && (allocator_type) == gc::kAllocatorTypeTLAB) { \
-    mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, kRuntimePointerSize); \
+    mirror::Class* klass = method->GetDexCacheResolvedType<false>(dex::TypeIndex(type_idx), \
+                                                                  kRuntimePointerSize); \
     if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \
       size_t byte_count = klass->GetObjectSize(); \
       byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
@@ -51,7 +53,10 @@
       } \
     } \
   } \
-  return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \
+  return AllocObjectFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \
+                                                       method, \
+                                                       self, \
+                                                       allocator_type); \
 } \
 extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \
     mirror::Class* klass, ArtMethod* method ATTRIBUTE_UNUSED, Thread* self) \
@@ -101,13 +106,19 @@
     uint32_t type_idx, ArtMethod* method, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \
+  return AllocObjectFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \
+                                                      method, \
+                                                      self, \
+                                                      allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \
     uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCode<false, instrumented_bool>(type_idx, component_count, method, self, \
+  return AllocArrayFromCode<false, instrumented_bool>(dex::TypeIndex(type_idx), \
+                                                      component_count, \
+                                                      method, \
+                                                      self, \
                                                       allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
@@ -121,7 +132,10 @@
     uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCode<true, instrumented_bool>(type_idx, component_count, method, self, \
+  return AllocArrayFromCode<true, instrumented_bool>(dex::TypeIndex(type_idx), \
+                                                     component_count, \
+                                                     method, \
+                                                     self, \
                                                      allocator_type); \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
@@ -129,9 +143,19 @@
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, false, allocator_type); \
+    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
+                                      component_count, \
+                                      method, \
+                                      self, \
+                                      false, \
+                                      allocator_type); \
   } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, false, allocator_type); \
+    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
+                                                  component_count, \
+                                                  method, \
+                                                  self, \
+                                                  false, \
+                                                  allocator_type); \
   } \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
@@ -139,9 +163,19 @@
     REQUIRES_SHARED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (!(instrumented_bool)) { \
-    return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, true, allocator_type); \
+    return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
+                                      component_count, \
+                                      method, \
+                                      self, \
+                                      true, \
+                                      allocator_type); \
   } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, true, allocator_type); \
+    return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
+                                                  component_count, \
+                                                  method, \
+                                                  self, \
+                                                  true, \
+                                                  allocator_type); \
   } \
 } \
 extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index d438418..b1259e1 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -20,6 +20,7 @@
 #include "class_linker-inl.h"
 #include "class_table-inl.h"
 #include "dex_file-inl.h"
+#include "dex_file_types.h"
 #include "gc/heap.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -37,7 +38,7 @@
   // given by inheritance.
   ScopedQuickEntrypointChecks sqec(self);
   auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly);
-  return ResolveVerifyAndClinit(type_idx, caller, self, true, false);
+  return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false);
 }
 
 extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self)
@@ -45,7 +46,7 @@
   // Called when method->dex_cache_resolved_types_[] misses.
   ScopedQuickEntrypointChecks sqec(self);
   auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly);
-  return ResolveVerifyAndClinit(type_idx, caller, self, false, false);
+  return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false);
 }
 
 extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self)
@@ -54,7 +55,7 @@
   // unpopulated.
   ScopedQuickEntrypointChecks sqec(self);
   auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kSaveRefsOnly);
-  return ResolveVerifyAndClinit(type_idx, caller, self, false, true);
+  return ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true);
 }
 
 extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index b0463d7..1283660 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -94,8 +94,8 @@
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_begin, stack_size, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, stack_trace_sample, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_trace_sample, wait_next, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_size, deps_or_stack_trace_sample, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deps_or_stack_trace_sample, wait_next, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, wait_next, monitor_enter_object, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, sizeof(void*));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3e1cbeb..ae9741f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -264,6 +264,10 @@
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     LOG(INFO) << "Heap() entering";
   }
+  if (kUseReadBarrier) {
+    CHECK_EQ(foreground_collector_type_, kCollectorTypeCC);
+    CHECK_EQ(background_collector_type_, kCollectorTypeCCBackground);
+  }
   CHECK_GE(large_object_threshold, kMinLargeObjectThreshold);
   ScopedTrace trace(__FUNCTION__);
   Runtime* const runtime = Runtime::Current();
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 0b602e9..6019540 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1002,7 +1002,7 @@
         mirror::IfTable* iftable = as_klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
         // Ensure iftable arrays are fixed up since we need GetMethodArray to return the valid
         // contents.
-        if (iftable != nullptr && IsInAppImage(iftable)) {
+        if (IsInAppImage(iftable)) {
           operator()(iftable);
           for (int32_t i = 0, count = iftable->Count(); i < count; ++i) {
             if (iftable->GetMethodArrayCount<kVerifyNone, kWithoutReadBarrier>(i) > 0) {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a32c800..1b3d339 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -543,7 +543,7 @@
           ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
           // This is a suspend point. But it's ok since value has been set into shadow_frame.
           ObjPtr<mirror::Class> klass = class_linker->ResolveType(
-              instr->VRegB_21c(), shadow_frame->GetMethod());
+              dex::TypeIndex(instr->VRegB_21c()), shadow_frame->GetMethod());
           DCHECK(klass->IsStringClass());
         }
       } else {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index cb775cd..72dbe6a 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -427,7 +427,7 @@
         if (!reg->VerifierInstanceOf(field_class.Ptr())) {
           // This should never happen.
           std::string temp1, temp2, temp3;
-          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                    "Put '%s' that is not instance of field '%s' in '%s'",
                                    reg->GetClass()->GetDescriptor(&temp1),
                                    field_class->GetDescriptor(&temp2),
@@ -922,7 +922,7 @@
         ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get());
         return false;
       }
-    } else {
+    } else if (!IsInvokeTransform(handle_kind)) {
       if (UNLIKELY(!IsCallerTransformer(callsite_type) &&
                    !callsite_type->IsConvertible(check_type.Ptr()))) {
         ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get());
@@ -990,37 +990,41 @@
       CHECK(called_method != nullptr);
     }
 
-    bool call_success;
-    if (handle_kind == kInvokeTransform) {
-      call_success = DoCallTransform<is_range>(called_method,
-                                               callsite_type,
-                                               handle_type,
-                                               self,
-                                               shadow_frame,
-                                               method_handle /* receiver */,
-                                               result,
-                                               arg,
-                                               first_src_reg);
+    if (IsInvokeTransform(handle_kind)) {
+      // There are two cases here - method handles representing regular
+      // transforms and those representing call site transforms. Method
+      // handles for call site transforms adapt their MethodType to match
+      // the call site. For these, the |callee_type| is the same as the
+      // |callsite_type|. The VarargsCollector is such a tranform, its
+      // method type depends on the call site, ie. x(a) or x(a, b), or
+      // x(a, b, c). The VarargsCollector invokes a variable arity method
+      // with the arity arguments in an array.
+      Handle<mirror::MethodType> callee_type =
+          (handle_kind == kInvokeCallSiteTransform) ? callsite_type : handle_type;
+      return DoCallTransform<is_range>(called_method,
+                                       callsite_type,
+                                       callee_type,
+                                       self,
+                                       shadow_frame,
+                                       method_handle /* receiver */,
+                                       result,
+                                       arg,
+                                       first_src_reg);
     } else {
-      call_success = DoCallPolymorphic<is_range>(called_method,
-                                                 callsite_type,
-                                                 handle_type,
-                                                 self,
-                                                 shadow_frame,
-                                                 result,
-                                                 arg,
-                                                 first_src_reg,
-                                                 handle_kind);
+      return DoCallPolymorphic<is_range>(called_method,
+                                         callsite_type,
+                                         handle_type,
+                                         self,
+                                         shadow_frame,
+                                         result,
+                                         arg,
+                                         first_src_reg,
+                                         handle_kind);
     }
-    if (LIKELY(call_success && ConvertReturnValue(callsite_type, handle_type, result))) {
-      return true;
-    }
-    DCHECK(self->IsExceptionPending());
-    return false;
   } else {
     DCHECK(!is_range);
     ArtField* field = method_handle->GetTargetField();
-    Primitive::Type field_type = field->GetTypeAsPrimitiveType();;
+    Primitive::Type field_type = field->GetTypeAsPrimitiveType();
 
     switch (handle_kind) {
       case kInstanceGet: {
@@ -1097,7 +1101,6 @@
   return num_ins;
 }
 
-
 inline void PerformCall(Thread* self,
                         const DexFile::CodeItem* code_item,
                         ArtMethod* caller_method,
@@ -1251,18 +1254,31 @@
   }
 
   PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+  if (self->IsExceptionPending()) {
+    return false;
+  }
 
   // If the caller of this signature polymorphic method was a transformer,
   // we need to copy the result back out to the emulated stack frame.
-  if (is_caller_transformer && !self->IsExceptionPending()) {
-    ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
-        reinterpret_cast<mirror::EmulatedStackFrame*>(
-            shadow_frame.GetVRegReference(first_src_reg)));
+  if (is_caller_transformer) {
+    StackHandleScope<2> hs(self);
+    Handle<mirror::EmulatedStackFrame> emulated_stack_frame(
+        hs.NewHandle(reinterpret_cast<mirror::EmulatedStackFrame*>(
+            shadow_frame.GetVRegReference(first_src_reg))));
+    Handle<mirror::MethodType> emulated_stack_type(hs.NewHandle(emulated_stack_frame->GetType()));
+    JValue local_result;
+    local_result.SetJ(result->GetJ());
 
-    emulated_stack_frame->SetReturnValue(self, *result);
+    if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) {
+      emulated_stack_frame->SetReturnValue(self, local_result);
+      return true;
+    } else {
+      DCHECK(self->IsExceptionPending());
+      return false;
+    }
+  } else {
+    return ConvertReturnValue(callsite_type, target_type, result);
   }
-
-  return !self->IsExceptionPending();
 }
 
 template <bool is_range>
@@ -1329,14 +1345,14 @@
               0 /* first dest reg */,
               new_shadow_frame,
               result);
+  if (self->IsExceptionPending()) {
+    return false;
+  }
 
   // If the called transformer method we called has returned a value, then we
   // need to copy it back to |result|.
-  if (!self->IsExceptionPending()) {
-    sf->GetReturnValue(self, result);
-  }
-
-  return !self->IsExceptionPending();
+  sf->GetReturnValue(self, result);
+  return ConvertReturnValue(callsite_type, callee_type, result);
 }
 
 template <bool is_range,
@@ -1460,7 +1476,7 @@
           ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
           if (do_assignability_check && o != nullptr) {
             PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-            const uint32_t type_idx = params->GetTypeItem(shorty_pos).type_idx_;
+            const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
             ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
                                                                              pointer_size);
             if (arg_type == nullptr) {
@@ -1477,7 +1493,7 @@
             if (!o->VerifierInstanceOf(arg_type)) {
               // This should never happen.
               std::string temp1, temp2;
-              self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+              self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                        "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
                                        new_shadow_frame->GetMethod()->GetName(), shorty_pos,
                                        o->GetClass()->GetDescriptor(&temp1),
@@ -1568,7 +1584,7 @@
     return false;
   }
   uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
-  ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(type_idx,
+  ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(dex::TypeIndex(type_idx),
                                                              shadow_frame.GetMethod(),
                                                              self,
                                                              false,
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 435ac62..22c0fe0 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -287,7 +287,7 @@
           if (!obj_result->VerifierInstanceOf(return_type)) {
             // This should never happen.
             std::string temp1, temp2;
-            self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+            self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                      "Returning '%s' that is not instance of return type '%s'",
                                      obj_result->GetClass()->GetDescriptor(&temp1),
                                      return_type->GetDescriptor(&temp2));
@@ -395,7 +395,7 @@
       }
       case Instruction::CONST_CLASS: {
         PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
                                                          shadow_frame.GetMethod(),
                                                          self,
                                                          false,
@@ -434,7 +434,7 @@
       }
       case Instruction::CHECK_CAST: {
         PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
                                                          shadow_frame.GetMethod(),
                                                          self,
                                                          false,
@@ -454,7 +454,7 @@
       }
       case Instruction::INSTANCE_OF: {
         PREAMBLE();
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegC_22c(),
+        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()),
                                                          shadow_frame.GetMethod(),
                                                          self,
                                                          false,
@@ -484,7 +484,7 @@
       case Instruction::NEW_INSTANCE: {
         PREAMBLE();
         ObjPtr<mirror::Object> obj = nullptr;
-        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+        ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
                                                          shadow_frame.GetMethod(),
                                                          self,
                                                          false,
@@ -495,8 +495,10 @@
             obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
           } else {
             obj = AllocObjectFromCode<do_access_check, true>(
-              inst->VRegB_21c(), shadow_frame.GetMethod(), self,
-              Runtime::Current()->GetHeap()->GetCurrentAllocator());
+                dex::TypeIndex(inst->VRegB_21c()),
+                shadow_frame.GetMethod(),
+                self,
+                Runtime::Current()->GetHeap()->GetCurrentAllocator());
           }
         }
         if (UNLIKELY(obj == nullptr)) {
@@ -520,7 +522,10 @@
         PREAMBLE();
         int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
         ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
-            inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
+            dex::TypeIndex(inst->VRegC_22c()),
+            length,
+            shadow_frame.GetMethod(),
+            self,
             Runtime::Current()->GetHeap()->GetCurrentAllocator());
         if (UNLIKELY(obj == nullptr)) {
           HANDLE_PENDING_EXCEPTION();
@@ -572,7 +577,7 @@
         } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
           // This should never happen.
           std::string temp;
-          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                    "Throwing '%s' that is not instance of Throwable",
                                    exception->GetClass()->GetDescriptor(&temp));
         } else {
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 2bd47bb..fbfed40 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -304,7 +304,11 @@
                                   ShadowFrame* shadow_frame,
                                   Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* c = ResolveVerifyAndClinit(index, shadow_frame->GetMethod(), self, false, false);
+  mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(index),
+                                            shadow_frame->GetMethod(),
+                                            self,
+                                            false,
+                                            false);
   if (UNLIKELY(c == nullptr)) {
     return true;
   }
@@ -317,7 +321,11 @@
                                  art::ArtMethod* method,
                                  Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false);
+  ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index),
+                                                   method,
+                                                   self,
+                                                   false,
+                                                   false);
   if (UNLIKELY(c == nullptr)) {
     return true;
   }
@@ -335,7 +343,11 @@
                                   art::ArtMethod* method,
                                   Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false);
+  ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index),
+                                                   method,
+                                                   self,
+                                                   false,
+                                                   false);
   if (UNLIKELY(c == nullptr)) {
     return false;  // Caller will check for pending exception.  Return value unimportant.
   }
@@ -353,7 +365,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr());
   mirror::Object* obj = nullptr;
-  mirror::Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+  mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
                                             shadow_frame->GetMethod(),
                                             self,
                                             false,
@@ -363,9 +375,10 @@
       gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
       obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
     } else {
-      obj = AllocObjectFromCode<false, true>(
-        inst->VRegB_21c(), shadow_frame->GetMethod(), self,
-        Runtime::Current()->GetHeap()->GetCurrentAllocator());
+      obj = AllocObjectFromCode<false, true>(dex::TypeIndex(inst->VRegB_21c()),
+                                             shadow_frame->GetMethod(),
+                                             self,
+                                             Runtime::Current()->GetHeap()->GetCurrentAllocator());
     }
   }
   if (UNLIKELY(obj == nullptr)) {
@@ -446,7 +459,7 @@
   const Instruction* inst = Instruction::At(dex_pc_ptr);
   int32_t length = shadow_frame->GetVReg(inst->VRegB_22c(inst_data));
   mirror::Object* obj = AllocArrayFromCode<false, true>(
-      inst->VRegC_22c(), length, shadow_frame->GetMethod(), self,
+      dex::TypeIndex(inst->VRegC_22c()), length, shadow_frame->GetMethod(), self,
       Runtime::Current()->GetHeap()->GetCurrentAllocator());
   if (UNLIKELY(obj == nullptr)) {
       return false;
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 2257fd6..a5b1038 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -424,7 +424,7 @@
 
   std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(jar_file.c_str(), error_msg));
   if (zip_archive == nullptr) {
-    return nullptr;;
+    return nullptr;
   }
   std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(entry_name, error_msg));
   if (zip_entry == nullptr) {
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index fad7d90..5574a11 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -621,8 +621,8 @@
   Thread* const self = Thread::Current();
   self->AssertThreadSuspensionIsAllowable();
   CHECK(pReq != nullptr);
+  CHECK_EQ(threadId, Dbg::GetThreadSelfId()) << "Only the current thread can suspend itself";
   /* send request and possibly suspend ourselves */
-  JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
   ScopedThreadSuspension sts(self, kWaitingForDebuggerSend);
   if (suspend_policy != SP_NONE) {
     AcquireJdwpTokenForEvent(threadId);
@@ -631,7 +631,7 @@
   {
     // Before suspending, we change our state to kSuspended so the debugger sees us as RUNNING.
     ScopedThreadStateChange stsc(self, kSuspended);
-    SuspendByPolicy(suspend_policy, thread_self_id);
+    SuspendByPolicy(suspend_policy, threadId);
   }
 }
 
@@ -658,13 +658,10 @@
 }
 
 void JdwpState::AcquireJdwpTokenForEvent(ObjectId threadId) {
-  CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
-  CHECK_NE(debug_thread_id_, threadId) << "Not expected debug thread";
   SetWaitForJdwpToken(threadId);
 }
 
 void JdwpState::ReleaseJdwpTokenForEvent() {
-  CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
   ClearWaitForJdwpToken();
 }
 
@@ -685,23 +682,28 @@
   /* this is held for very brief periods; contention is unlikely */
   MutexLock mu(self, jdwp_token_lock_);
 
-  CHECK_NE(jdwp_token_owner_thread_id_, threadId) << "Thread is already holding event thread lock";
+  if (jdwp_token_owner_thread_id_ == threadId) {
+    // Only the debugger thread may already hold the event token. For instance, it may trigger
+    // a CLASS_PREPARE event while processing a command that initializes a class.
+    CHECK_EQ(threadId, debug_thread_id_) << "Non-debugger thread is already holding event token";
+  } else {
+    /*
+     * If another thread is already doing stuff, wait for it.  This can
+     * go to sleep indefinitely.
+     */
 
-  /*
-   * If another thread is already doing stuff, wait for it.  This can
-   * go to sleep indefinitely.
-   */
-  while (jdwp_token_owner_thread_id_ != 0) {
-    VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping",
-                               jdwp_token_owner_thread_id_, threadId);
-    waited = true;
-    jdwp_token_cond_.Wait(self);
-  }
+    while (jdwp_token_owner_thread_id_ != 0) {
+      VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping",
+                                 jdwp_token_owner_thread_id_, threadId);
+      waited = true;
+      jdwp_token_cond_.Wait(self);
+    }
 
-  if (waited || threadId != debug_thread_id_) {
-    VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId);
+    if (waited || threadId != debug_thread_id_) {
+      VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId);
+    }
+    jdwp_token_owner_thread_id_ = threadId;
   }
-  jdwp_token_owner_thread_id_ = threadId;
 }
 
 /*
@@ -1224,14 +1226,15 @@
     VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
   }
 
-  if (thread_id == debug_thread_id_) {
+  ObjectId reported_thread_id = thread_id;
+  if (reported_thread_id == debug_thread_id_) {
     /*
      * JDWP says that, for a class prep in the debugger thread, we
      * should set thread to null and if any threads were supposed
      * to be suspended then we suspend all other threads.
      */
     VLOG(jdwp) << "  NOTE: class prepare in debugger thread!";
-    thread_id = 0;
+    reported_thread_id = 0;
     if (suspend_policy == SP_EVENT_THREAD) {
       suspend_policy = SP_ALL;
     }
@@ -1244,7 +1247,7 @@
   for (const JdwpEvent* pEvent : match_list) {
     expandBufAdd1(pReq, pEvent->eventKind);
     expandBufAdd4BE(pReq, pEvent->requestId);
-    expandBufAddObjectId(pReq, thread_id);
+    expandBufAddObjectId(pReq, reported_thread_id);
     expandBufAdd1(pReq, tag);
     expandBufAddRefTypeId(pReq, class_id);
     expandBufAddUtf8String(pReq, signature);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 4c10063..dac2e60 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -114,7 +114,7 @@
   } else {
     jit_options->invoke_transition_weight_ = std::max(
         jit_options->warmup_threshold_ / Jit::kDefaultInvokeTransitionWeightRatio,
-        static_cast<size_t>(1));;
+        static_cast<size_t>(1));
   }
 
   return jit_options;
@@ -274,6 +274,15 @@
               << ArtMethod::PrettyMethod(method_to_compile)
               << " osr=" << std::boolalpha << osr;
   }
+  if (kIsDebugBuild) {
+    if (self->IsExceptionPending()) {
+      mirror::Throwable* exception = self->GetException();
+      LOG(FATAL) << "No pending exception expected after compiling "
+                 << ArtMethod::PrettyMethod(method)
+                 << ": "
+                 << exception->Dump();
+    }
+  }
   return success;
 }
 
@@ -701,5 +710,24 @@
   }
 }
 
+ScopedJitSuspend::ScopedJitSuspend() {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr);
+  if (was_on_) {
+    Thread* self = Thread::Current();
+    jit->WaitForCompilationToFinish(self);
+    jit->GetThreadPool()->StopWorkers(self);
+    jit->WaitForCompilationToFinish(self);
+  }
+}
+
+ScopedJitSuspend::~ScopedJitSuspend() {
+  if (was_on_) {
+    DCHECK(Runtime::Current()->GetJit() != nullptr);
+    DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr);
+    Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current());
+  }
+}
+
 }  // namespace jit
 }  // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a782437..a230c78 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -175,6 +175,10 @@
 
   static bool LoadCompilerLibrary(std::string* error_msg);
 
+  ThreadPool* GetThreadPool() const {
+    return thread_pool_.get();
+  }
+
  private:
   Jit();
 
@@ -278,6 +282,16 @@
   DISALLOW_COPY_AND_ASSIGN(JitOptions);
 };
 
+// Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce.
+class ScopedJitSuspend {
+ public:
+  ScopedJitSuspend();
+  ~ScopedJitSuspend();
+
+ private:
+  bool was_on_;
+};
+
 }  // namespace jit
 }  // namespace art
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index a26d850..3531852 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -80,8 +80,18 @@
 
   std::string error_str;
   // Map name specific for android_os_Debug.cpp accounting.
+  // Map in low 4gb to simplify accessing root tables for x86_64.
+  // We could do PC-relative addressing to avoid this problem, but that
+  // would require reserving code and data area before submitting, which
+  // means more windows for the code memory to be RWX.
   MemMap* data_map = MemMap::MapAnonymous(
-      "data-code-cache", nullptr, max_capacity, kProtAll, false, false, &error_str, use_ashmem);
+      "data-code-cache", nullptr,
+      max_capacity,
+      kProtAll,
+      /* low_4gb */ true,
+      /* reuse */ false,
+      &error_str,
+      use_ashmem);
   if (data_map == nullptr) {
     std::ostringstream oss;
     oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
@@ -197,34 +207,40 @@
 
 uint8_t* JitCodeCache::CommitCode(Thread* self,
                                   ArtMethod* method,
-                                  const uint8_t* vmap_table,
+                                  uint8_t* stack_map,
+                                  uint8_t* roots_data,
                                   size_t frame_size_in_bytes,
                                   size_t core_spill_mask,
                                   size_t fp_spill_mask,
                                   const uint8_t* code,
                                   size_t code_size,
-                                  bool osr) {
+                                  bool osr,
+                                  Handle<mirror::ObjectArray<mirror::Object>> roots) {
   uint8_t* result = CommitCodeInternal(self,
                                        method,
-                                       vmap_table,
+                                       stack_map,
+                                       roots_data,
                                        frame_size_in_bytes,
                                        core_spill_mask,
                                        fp_spill_mask,
                                        code,
                                        code_size,
-                                       osr);
+                                       osr,
+                                       roots);
   if (result == nullptr) {
     // Retry.
     GarbageCollectCache(self);
     result = CommitCodeInternal(self,
                                 method,
-                                vmap_table,
+                                stack_map,
+                                roots_data,
                                 frame_size_in_bytes,
                                 core_spill_mask,
                                 fp_spill_mask,
                                 code,
                                 code_size,
-                                osr);
+                                osr,
+                                roots);
   }
   return result;
 }
@@ -243,20 +259,82 @@
   return reinterpret_cast<uintptr_t>(code) - RoundUp(sizeof(OatQuickMethodHeader), alignment);
 }
 
+static uint32_t ComputeRootTableSize(uint32_t number_of_roots) {
+  return sizeof(uint32_t) + number_of_roots * sizeof(GcRoot<mirror::Object>);
+}
+
+static uint32_t GetNumberOfRoots(const uint8_t* stack_map) {
+  // The length of the table is stored just before the stack map (and therefore at the end of
+  // the table itself), in order to be able to fetch it from a `stack_map` pointer.
+  return reinterpret_cast<const uint32_t*>(stack_map)[-1];
+}
+
+static void FillRootTableLength(uint8_t* roots_data, uint32_t length) {
+  // Store the length of the table at the end. This will allow fetching it from a `stack_map`
+  // pointer.
+  reinterpret_cast<uint32_t*>(roots_data)[length] = length;
+}
+
+static void FillRootTable(uint8_t* roots_data, Handle<mirror::ObjectArray<mirror::Object>> roots)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  GcRoot<mirror::Object>* gc_roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data);
+  const uint32_t length = roots->GetLength();
+  // Put all roots in `roots_data`.
+  for (uint32_t i = 0; i < length; ++i) {
+    ObjPtr<mirror::Object> object = roots->Get(i);
+    if (kIsDebugBuild) {
+      // Ensure the string is strongly interned. b/32995596
+      CHECK(object->IsString());
+      ObjPtr<mirror::String> str = reinterpret_cast<mirror::String*>(object.Ptr());
+      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+      CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr);
+    }
+    gc_roots[i] = GcRoot<mirror::Object>(object);
+  }
+  FillRootTableLength(roots_data, length);
+}
+
+static uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr) {
+  OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+  uint8_t* data = method_header->GetOptimizedCodeInfoPtr();
+  uint32_t roots = GetNumberOfRoots(data);
+  if (number_of_roots != nullptr) {
+    *number_of_roots = roots;
+  }
+  return data - ComputeRootTableSize(roots);
+}
+
+void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) {
+  MutexLock mu(Thread::Current(), lock_);
+  for (const auto& entry : method_code_map_) {
+    uint32_t number_of_roots = 0;
+    uint8_t* roots_data = GetRootTable(entry.first, &number_of_roots);
+    GcRoot<mirror::Object>* roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data);
+    for (uint32_t i = 0; i < number_of_roots; ++i) {
+      // This does not need a read barrier because this is called by GC.
+      mirror::Object* object = roots[i].Read<kWithoutReadBarrier>();
+      DCHECK(object != nullptr);
+      mirror::Object* new_object = visitor->IsMarked(object);
+      // We know the string is marked because it's a strongly-interned string that
+      // is always alive. The IsMarked implementation of the CMS collector returns
+      // null for newly allocated objects, but we know those haven't moved. Therefore,
+      // only update the entry if we get a different non-null string.
+      // TODO: Do not use IsMarked for j.l.Class, and adjust once we move this method
+      // out of the weak access/creation pause. b/32167580
+      if (new_object != nullptr && new_object != object) {
+        DCHECK(new_object->IsString());
+        roots[i] = GcRoot<mirror::Object>(new_object);
+      }
+    }
+  }
+}
+
 void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) {
   uintptr_t allocation = FromCodeToAllocation(code_ptr);
-  const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
   DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr));
-
-  // Use the offset directly to prevent sanity check that the method is
-  // compiled with optimizing.
-  // TODO(ngeoffray): Clean up.
-  if (method_header->vmap_table_offset_ != 0) {
-    const uint8_t* data = method_header->code_ - method_header->vmap_table_offset_;
-    FreeData(const_cast<uint8_t*>(data));
-  }
+  FreeData(GetRootTable(code_ptr));
   FreeCode(reinterpret_cast<uint8_t*>(allocation));
 }
 
@@ -308,17 +386,21 @@
 
 uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
                                           ArtMethod* method,
-                                          const uint8_t* vmap_table,
+                                          uint8_t* stack_map,
+                                          uint8_t* roots_data,
                                           size_t frame_size_in_bytes,
                                           size_t core_spill_mask,
                                           size_t fp_spill_mask,
                                           const uint8_t* code,
                                           size_t code_size,
-                                          bool osr) {
+                                          bool osr,
+                                          Handle<mirror::ObjectArray<mirror::Object>> roots) {
+  DCHECK(stack_map != nullptr);
   size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
   // Ensure the header ends up at expected instruction alignment.
   size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
   size_t total_size = header_size + code_size;
+  const uint32_t num_roots = roots->GetLength();
 
   OatQuickMethodHeader* method_header = nullptr;
   uint8_t* code_ptr = nullptr;
@@ -331,6 +413,9 @@
       ScopedCodeCacheWrite scc(code_map_.get());
       memory = AllocateCode(total_size);
       if (memory == nullptr) {
+        // Fill root table length so that ClearData works correctly in case of failure. Otherwise
+        // the length will be 0 and cause incorrect DCHECK failure.
+        FillRootTableLength(roots_data, num_roots);
         return nullptr;
       }
       code_ptr = memory + header_size;
@@ -338,21 +423,29 @@
       std::copy(code, code + code_size, code_ptr);
       method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
       new (method_header) OatQuickMethodHeader(
-          (vmap_table == nullptr) ? 0 : code_ptr - vmap_table,
+          code_ptr - stack_map,
           frame_size_in_bytes,
           core_spill_mask,
           fp_spill_mask,
           code_size);
+      // Flush caches before we remove write permission because on some ARMv8 hardware,
+      // flushing caches require write permissions.
+      //
+      // For reference, here are kernel patches discussing about this issue:
+      // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279
+      // https://patchwork.kernel.org/patch/9047921/
+      FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+                            reinterpret_cast<char*>(code_ptr + code_size));
     }
 
-    FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
-                          reinterpret_cast<char*>(code_ptr + code_size));
     number_of_compilations_++;
   }
   // We need to update the entry point in the runnable state for the instrumentation.
   {
     MutexLock mu(self, lock_);
     method_code_map_.Put(code_ptr, method);
+    // Fill the root table before updating the entry point.
+    FillRootTable(roots_data, roots);
     if (osr) {
       number_of_osr_compilations_++;
       osr_code_map_.Put(method, code_ptr);
@@ -403,13 +496,26 @@
   return used_memory_for_data_;
 }
 
-void JitCodeCache::ClearData(Thread* self, void* data) {
-  MutexLock mu(self, lock_);
-  FreeData(reinterpret_cast<uint8_t*>(data));
+static const uint8_t* FromStackMapToRoots(const uint8_t* stack_map_data) {
+  return stack_map_data - ComputeRootTableSize(GetNumberOfRoots(stack_map_data));
 }
 
-uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size, ArtMethod* method) {
-  size = RoundUp(size, sizeof(void*));
+void JitCodeCache::ClearData(Thread* self,
+                             uint8_t* stack_map_data,
+                             uint8_t* roots_data) {
+  DCHECK_EQ(FromStackMapToRoots(stack_map_data), roots_data);
+  MutexLock mu(self, lock_);
+  FreeData(reinterpret_cast<uint8_t*>(roots_data));
+}
+
+void JitCodeCache::ReserveData(Thread* self,
+                               size_t stack_map_size,
+                               size_t number_of_roots,
+                               ArtMethod* method,
+                               uint8_t** stack_map_data,
+                               uint8_t** roots_data) {
+  size_t table_size = ComputeRootTableSize(number_of_roots);
+  size_t size = RoundUp(stack_map_size + table_size, sizeof(void*));
   uint8_t* result = nullptr;
 
   {
@@ -436,7 +542,8 @@
               << " for stack maps of "
               << ArtMethod::PrettyMethod(method);
   }
-  return result;
+  *roots_data = result;
+  *stack_map_data = result + table_size;
 }
 
 class MarkCodeVisitor FINAL : public StackVisitor {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index e15c93a..40112fe 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -92,13 +92,15 @@
   // Allocate and write code and its metadata to the code cache.
   uint8_t* CommitCode(Thread* self,
                       ArtMethod* method,
-                      const uint8_t* vmap_table,
+                      uint8_t* stack_map,
+                      uint8_t* roots_data,
                       size_t frame_size_in_bytes,
                       size_t core_spill_mask,
                       size_t fp_spill_mask,
                       const uint8_t* code,
                       size_t code_size,
-                      bool osr)
+                      bool osr,
+                      Handle<mirror::ObjectArray<mirror::Object>> roots)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!lock_);
 
@@ -108,13 +110,19 @@
   // Return true if the code cache contains this method.
   bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_);
 
-  // Reserve a region of data of size at least "size". Returns null if there is no more room.
-  uint8_t* ReserveData(Thread* self, size_t size, ArtMethod* method)
+  // Allocate a region of data that contain `size` bytes, and potentially space
+  // for storing `number_of_roots` roots. Returns null if there is no more room.
+  void ReserveData(Thread* self,
+                   size_t size,
+                   size_t number_of_roots,
+                   ArtMethod* method,
+                   uint8_t** stack_map_data,
+                   uint8_t** roots_data)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!lock_);
 
   // Clear data from the data portion of the code cache.
-  void ClearData(Thread* self, void* data)
+  void ClearData(Thread* self, uint8_t* stack_map_data, uint8_t* roots_data)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!lock_);
 
@@ -188,6 +196,10 @@
 
   bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_);
 
+  void SweepRootTables(IsMarkedVisitor* visitor)
+      REQUIRES(!lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   // Take ownership of maps.
   JitCodeCache(MemMap* code_map,
@@ -201,13 +213,15 @@
   // allocation fails. Return null if the allocation fails.
   uint8_t* CommitCodeInternal(Thread* self,
                               ArtMethod* method,
-                              const uint8_t* vmap_table,
+                              uint8_t* stack_map,
+                              uint8_t* roots_data,
                               size_t frame_size_in_bytes,
                               size_t core_spill_mask,
                               size_t fp_spill_mask,
                               const uint8_t* code,
                               size_t code_size,
-                              bool osr)
+                              bool osr,
+                              Handle<mirror::ObjectArray<mirror::Object>> roots)
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index f535151..6f2a8c6 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -37,7 +37,7 @@
 namespace art {
 
 const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' };
 
 static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
 
@@ -235,7 +235,7 @@
       AddUintToBuffer(&buffer, method_it);
     }
     for (auto class_id : dex_data.class_set) {
-      AddUintToBuffer(&buffer, class_id);
+      AddUintToBuffer(&buffer, class_id.index_);
     }
     DCHECK_EQ(required_capacity, buffer.size())
         << "Failed to add the expected number of bytes in the buffer";
@@ -282,12 +282,12 @@
 
 bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
                                            uint32_t checksum,
-                                           uint16_t class_idx) {
+                                           dex::TypeIndex type_idx) {
   DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
   if (data == nullptr) {
     return false;
   }
-  data->class_set.insert(class_idx);
+  data->class_set.insert(type_idx);
   return true;
 }
 
@@ -304,8 +304,8 @@
   }
 
   for (uint16_t i = 0; i < class_set_size; i++) {
-    uint16_t class_def_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
-    if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
+    uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
+    if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) {
       return false;
     }
   }
@@ -569,14 +569,14 @@
   return false;
 }
 
-bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const {
+bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
   auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
   if (info_it != info_.end()) {
     if (!ChecksumMatch(dex_file, info_it->second.checksum)) {
       return false;
     }
-    const std::set<uint16_t>& classes = info_it->second.class_set;
-    return classes.find(class_def_idx) != classes.end();
+    const std::set<dex::TypeIndex>& classes = info_it->second.class_set;
+    return classes.find(type_idx) != classes.end();
   }
   return false;
 }
@@ -637,7 +637,7 @@
     os << "\n\tclasses: ";
     for (const auto class_it : dex_data.class_set) {
       if (dex_file != nullptr) {
-        os << "\n\t\t" << dex_file->GetClassDescriptor(dex_file->GetClassDef(class_it));
+        os << "\n\t\t" << dex_file->PrettyType(class_it);
       } else {
         os << class_it << ",";
       }
@@ -702,11 +702,11 @@
     }
 
     for (uint16_t c = 0; c < number_of_classes; c++) {
-      uint16_t class_idx = rand() % max_classes;
+      uint16_t type_idx = rand() % max_classes;
       if (c < (number_of_classes / kFavorSplit)) {
-        class_idx %= kFavorFirstN;
+        type_idx %= kFavorFirstN;
       }
-      info.AddClassIndex(profile_key, 0, class_idx);
+      info.AddClassIndex(profile_key, 0, dex::TypeIndex(type_idx));
     }
   }
   return info.Save(fd);
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index fdca078..4136488 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -23,6 +23,7 @@
 #include "atomic.h"
 #include "dex_cache_resolved_classes.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "method_reference.h"
 #include "safe_map.h"
 
@@ -65,8 +66,8 @@
   // Returns true if the method reference is present in the profiling info.
   bool ContainsMethod(const MethodReference& method_ref) const;
 
-  // Returns true if the class is present in the profiling info.
-  bool ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const;
+  // Returns true if the class's type is present in the profiling info.
+  bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
 
   // Dumps all the loaded profile info into a string and returns it.
   // If dex_files is not null then the method indices will be resolved to their
@@ -104,7 +105,7 @@
     explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
     uint32_t checksum;
     std::set<uint16_t> method_set;
-    std::set<uint16_t> class_set;
+    std::set<dex::TypeIndex> class_set;
 
     bool operator==(const DexFileData& other) const {
       return checksum == other.checksum && method_set == other.method_set;
@@ -115,7 +116,7 @@
 
   DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
   bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
-  bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx);
+  bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx);
   bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
 
   // Parsing functionality.
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 54c772a..d0a4902 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -46,12 +46,13 @@
   kInvokeStatic,
   kInvokeInterface,
   kInvokeTransform,
+  kInvokeCallSiteTransform,
   kInstanceGet,
   kInstancePut,
   kStaticGet,
   kStaticPut,
   kLastValidKind = kStaticPut,
-  kLastInvokeKind = kInvokeTransform
+  kLastInvokeKind = kInvokeCallSiteTransform
 };
 
 // Whether the given method handle kind is some variant of an invoke.
@@ -59,6 +60,11 @@
   return handle_kind <= kLastInvokeKind;
 }
 
+// Whether the given method handle kind is some variant of a tranform.
+inline bool IsInvokeTransform(const MethodHandleKind handle_kind) {
+  return handle_kind == kInvokeTransform || handle_kind == kInvokeCallSiteTransform;
+}
+
 // Returns true if there is a possible conversion from |from| to |to|
 // for a MethodHandle parameter.
 bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from,
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index bbdb2af..aa5da2e 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -372,7 +372,7 @@
     // to access the field if the FieldId specifies an accessible subclass of the declaring
     // class rather than the declaring class itself.
     ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
-    uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+    dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
     // The referenced class has already been resolved with the field, but may not be in the dex
     // cache. Use LookupResolveType here to search the class table if it is not in the dex cache.
     // should be no thread suspension due to the class being resolved.
@@ -410,7 +410,7 @@
     // to access the method if the MethodId specifies an accessible subclass of the declaring
     // class rather than the declaring class itself.
     ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
-    uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+    dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
     // The referenced class has already been resolved with the method, but may not be in the dex
     // cache.
     ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
@@ -526,18 +526,17 @@
 template<VerifyObjectFlags kVerifyFlags,
          ReadBarrierOption kReadBarrierOption>
 inline IfTable* Class::GetIfTable() {
-  return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset());
+  ObjPtr<IfTable> ret = GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset());
+  DCHECK(ret != nullptr) << PrettyClass(this);
+  return ret.Ptr();
 }
 
 inline int32_t Class::GetIfTableCount() {
-  ObjPtr<IfTable> iftable = GetIfTable();
-  if (iftable == nullptr) {
-    return 0;
-  }
-  return iftable->Count();
+  return GetIfTable()->Count();
 }
 
 inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) {
+  DCHECK(new_iftable != nullptr) << PrettyClass(this);
   SetFieldObject<false>(IfTableOffset(), new_iftable);
 }
 
@@ -895,7 +894,8 @@
   klass->SetClassSize(class_size_);
   klass->SetPrimitiveType(Primitive::kPrimNot);  // Default to not being primitive.
   klass->SetDexClassDefIndex(DexFile::kDexNoIndex16);  // Default to no valid class def index.
-  klass->SetDexTypeIndex(DexFile::kDexNoIndex16);  // Default to no valid type index.
+  klass->SetDexTypeIndex(dex::TypeIndex(DexFile::kDexNoIndex16));  // Default to no valid type
+                                                                   // index.
   // Default to force slow path until initialized.
   klass->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max());
 }
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index db46027..0cfe29b 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -923,7 +923,7 @@
   return &GetDexFile().GetClassDef(class_def_idx);
 }
 
-uint16_t Class::GetDirectInterfaceTypeIdx(uint32_t idx) {
+dex::TypeIndex Class::GetDirectInterfaceTypeIdx(uint32_t idx) {
   DCHECK(!IsPrimitive());
   DCHECK(!IsArrayClass());
   return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_;
@@ -947,10 +947,11 @@
     DCHECK(interfaces != nullptr);
     return interfaces->Get(idx);
   } else {
-    uint16_t type_idx = klass->GetDirectInterfaceTypeIdx(idx);
+    dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx);
     ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx);
     if (interface == nullptr) {
-      interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), type_idx,
+      interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(),
+                                                                    type_idx,
                                                                     klass.Get());
       CHECK(interface != nullptr || self->IsExceptionPending());
     }
@@ -1130,10 +1131,12 @@
   return depth;
 }
 
-uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) {
+dex::TypeIndex Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) {
   std::string temp;
   const DexFile::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp));
-  return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id);
+  return (type_id == nullptr)
+      ? dex::TypeIndex(DexFile::kDexNoIndex)
+      : dex_file.GetIndexForTypeId(*type_id);
 }
 
 template <PointerSize kPointerSize, bool kTransactionActive>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 711914d..792f626 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -20,6 +20,7 @@
 #include "base/enums.h"
 #include "base/iteration_range.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "class_flags.h"
 #include "gc_root.h"
 #include "gc/allocator_type.h"
@@ -1148,16 +1149,17 @@
     SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx);
   }
 
-  uint16_t GetDexTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_));
+  dex::TypeIndex GetDexTypeIndex() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return dex::TypeIndex(
+        static_cast<uint16_t>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_))));
   }
 
-  void SetDexTypeIndex(uint16_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
+  void SetDexTypeIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
     // Not called within a transaction.
-    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx);
+    SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx.index_);
   }
 
-  uint32_t FindTypeIndexInOtherDexFile(const DexFile& dex_file)
+  dex::TypeIndex FindTypeIndexInOtherDexFile(const DexFile& dex_file)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   static Class* GetJavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1198,7 +1200,7 @@
 
   ALWAYS_INLINE uint32_t NumDirectInterfaces() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  dex::TypeIndex GetDirectInterfaceTypeIdx(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
   static ObjPtr<Class> GetDirectInterface(Thread* self,
                                           Handle<Class> klass,
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index c7a123b..d903f71 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -69,15 +69,15 @@
   }
 }
 
-inline Class* DexCache::GetResolvedType(uint32_t type_idx) {
-  DCHECK_LT(type_idx, NumResolvedTypes());
-  return GetResolvedTypes()[type_idx].Read();
+inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
+  DCHECK_LT(type_idx.index_, NumResolvedTypes());
+  return GetResolvedTypes()[type_idx.index_].Read();
 }
 
-inline void DexCache::SetResolvedType(uint32_t type_idx, ObjPtr<Class> resolved) {
-  DCHECK_LT(type_idx, NumResolvedTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
+inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
+  DCHECK_LT(type_idx.index_, NumResolvedTypes());  // NOTE: Unchecked, i.e. not throwing AIOOB.
   // TODO default transaction support.
-  GetResolvedTypes()[type_idx] = GcRoot<Class>(resolved);
+  GetResolvedTypes()[type_idx.index_] = GcRoot<Class>(resolved);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
 }
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 1ae694d..7d82d3a 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -21,6 +21,7 @@
 #include "art_field.h"
 #include "art_method.h"
 #include "class.h"
+#include "dex_file_types.h"
 #include "object.h"
 #include "object_array.h"
 
@@ -223,9 +224,9 @@
   // the string isn't kept live.
   void ClearString(uint32_t string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  Class* GetResolvedType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+  Class* GetResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetResolvedType(uint32_t type_idx, ObjPtr<Class> resolved)
+  void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size)
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
index 9fa06b7..d83a536 100644
--- a/runtime/mirror/emulated_stack_frame.h
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -58,6 +58,10 @@
   // Sets the return value slot of this emulated stack frame to |value|.
   void SetReturnValue(Thread* self, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, 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_);
@@ -67,10 +71,6 @@
     return static_class_.Read();
   }
 
-  mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
-  }
-
   mirror::ObjectArray<mirror::Object>* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldObject<mirror::ObjectArray<mirror::Object>>(
         OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_));
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index be5d446..0fdf132 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -150,12 +150,12 @@
       uintptr_t fake_address_dependency;
       if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
         baker_non_gray_case = true;
-        DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency;
+        DCHECK_EQ(fake_address_dependency, 0U);
         src.Assign(reinterpret_cast<ObjectArray<T>*>(
             reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency));
         for (int i = 0; i < count; ++i) {
           // We can skip the RB here because 'src' isn't gray.
-          Object* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>(
+          T* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>(
               src_pos + i);
           SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj);
         }
@@ -164,7 +164,7 @@
     if (!baker_non_gray_case) {
       for (int i = 0; i < count; ++i) {
         // We need a RB here. ObjectArray::GetWithoutChecks() contains a RB.
-        Object* obj = src->GetWithoutChecks(src_pos + i);
+        T* obj = src->GetWithoutChecks(src_pos + i);
         SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj);
       }
     }
@@ -175,12 +175,12 @@
       uintptr_t fake_address_dependency;
       if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
         baker_non_gray_case = true;
-        DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency;
+        DCHECK_EQ(fake_address_dependency, 0U);
         src.Assign(reinterpret_cast<ObjectArray<T>*>(
             reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency));
         for (int i = count - 1; i >= 0; --i) {
           // We can skip the RB here because 'src' isn't gray.
-          Object* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>(
+          T* obj = src->template GetWithoutChecks<kDefaultVerifyFlags, kWithoutReadBarrier>(
               src_pos + i);
           SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj);
         }
@@ -189,7 +189,7 @@
     if (!baker_non_gray_case) {
       for (int i = count - 1; i >= 0; --i) {
         // We need a RB here. ObjectArray::GetWithoutChecks() contains a RB.
-        Object* obj = src->GetWithoutChecks(src_pos + i);
+        T* obj = src->GetWithoutChecks(src_pos + i);
         SetWithoutChecksAndWriteBarrier<false>(dst_pos + i, obj);
       }
     }
@@ -225,7 +225,7 @@
     uintptr_t fake_address_dependency;
     if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
       baker_non_gray_case = true;
-      DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency;
+      DCHECK_EQ(fake_address_dependency, 0U);
       src.Assign(reinterpret_cast<ObjectArray<T>*>(
           reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency));
       for (int i = 0; i < count; ++i) {
@@ -266,14 +266,14 @@
   Class* dst_class = GetClass()->GetComponentType();
   Class* lastAssignableElementClass = dst_class;
 
-  Object* o = nullptr;
+  T* o = nullptr;
   int i = 0;
   bool baker_non_gray_case = false;
   if (kUseReadBarrier && kUseBakerReadBarrier) {
     uintptr_t fake_address_dependency;
     if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
       baker_non_gray_case = true;
-      DCHECK_EQ(fake_address_dependency, 0U) << fake_address_dependency;
+      DCHECK_EQ(fake_address_dependency, 0U);
       src.Assign(reinterpret_cast<ObjectArray<T>*>(
           reinterpret_cast<uintptr_t>(src.Ptr()) | fake_address_dependency));
       for (; i < count; ++i) {
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 5bf254d..4b47f7f 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -313,7 +313,7 @@
   ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize);
   const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
   ASSERT_TRUE(type_id != nullptr);
-  uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
+  dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
   Object* array = CheckAndAllocArrayFromCodeInstrumented(
       type_idx, 3, sort, Thread::Current(), false,
       Runtime::Current()->GetHeap()->GetCurrentAllocator());
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index b866a63..ade4e87 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -104,7 +104,7 @@
     CHECK_EQ(array_len % 2, 0);
     const auto depth = array_len / 2;
     if (depth == 0) {
-      result += "(Throwable with empty stack trace)";
+      result += "(Throwable with empty stack trace)\n";
     } else {
       const PointerSize ptr_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
       for (int32_t i = 0; i < depth; ++i) {
@@ -124,7 +124,7 @@
       ObjPtr<ObjectArray<StackTraceElement>> ste_array =
           ObjPtr<ObjectArray<StackTraceElement>>::DownCast(stack_trace);
       if (ste_array->GetLength() == 0) {
-        result += "(Throwable with empty stack trace)";
+        result += "(Throwable with empty stack trace)\n";
       } else {
         for (int32_t i = 0; i < ste_array->GetLength(); ++i) {
           StackTraceElement* ste = ste_array->Get(i);
@@ -139,7 +139,7 @@
         }
       }
     } else {
-      result += "(Throwable with no stack trace)";
+      result += "(Throwable with no stack trace)\n";
     }
   }
   ObjPtr<Throwable> cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_));
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index df0849a..1a77072 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -413,33 +413,7 @@
 
   OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
                                       false /* load_executable */);
-
-  std::ostringstream status;
-  bool oat_file_exists = false;
-  bool odex_file_exists = false;
-  if (oat_file_assistant.OatFileExists()) {
-    oat_file_exists = true;
-    status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
-    status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
-    status << ", status=" << oat_file_assistant.OatFileStatus();
-  }
-
-  if (oat_file_assistant.OdexFileExists()) {
-    odex_file_exists = true;
-    if (oat_file_exists) {
-      status << "] ";
-    }
-    status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
-    status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
-    status << ", status=" << oat_file_assistant.OdexFileStatus();
-  }
-
-  if (!oat_file_exists && !odex_file_exists) {
-    status << "invalid[";
-  }
-
-  status << "]";
-  return env->NewStringUTF(status.str().c_str());
+  return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
 }
 
 static jint DexFile_getDexOptNeeded(JNIEnv* env,
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 866dc7f..48feb11 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -34,6 +34,7 @@
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "dex_file_types.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/allocator/dlmalloc.h"
 #include "gc/heap.h"
@@ -305,7 +306,7 @@
 // Based on ClassLinker::ResolveType.
 static void PreloadDexCachesResolveType(Thread* self,
                                         ObjPtr<mirror::DexCache> dex_cache,
-                                        uint32_t type_idx)
+                                        dex::TypeIndex type_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(type_idx);
   if (klass != nullptr) {
@@ -455,7 +456,7 @@
       }
     }
     for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
-      ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(j);
+      ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(j));
       if (klass != nullptr) {
         filled->num_types++;
       }
@@ -519,7 +520,7 @@
 
     if (kPreloadDexCachesTypes) {
       for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) {
-        PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), j);
+        PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), dex::TypeIndex(j));
       }
     }
 
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 71379a5..f6de593 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -17,6 +17,7 @@
 #include "java_lang_DexCache.h"
 
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "jni_internal.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
@@ -53,7 +54,7 @@
   ScopedFastNativeObjectAccess soa(env);
   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));
+  return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index)));
 }
 
 static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index) {
@@ -68,7 +69,7 @@
   ScopedFastNativeObjectAccess soa(env);
   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(dex::TypeIndex(type_index), soa.Decode<mirror::Class>(type));
 }
 
 static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index,
diff --git a/runtime/oat.h b/runtime/oat.h
index 3aef707..8c84d42 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', '9', '1', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '9', '2', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c14b616..bdf8b0e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -38,6 +38,7 @@
 #include "base/stl_util.h"
 #include "base/systrace.h"
 #include "base/unix_file/fd_file.h"
+#include "dex_file_types.h"
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "gc_root.h"
@@ -718,7 +719,7 @@
     dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo);
 #else
     UNUSED(oat_file_begin);
-    static_assert(!kIsTargetBuild, "host_dlopen_handles_ will leak handles");
+    static_assert(!kIsTargetBuild || kIsTargetLinux, "host_dlopen_handles_ will leak handles");
     MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
     dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
     if (dlopen_handle_ != nullptr) {
@@ -1252,13 +1253,14 @@
     if (lookup_table_data_ + TypeLookupTable::RawDataLength(num_class_defs) > GetOatFile()->End()) {
       LOG(WARNING) << "found truncated lookup table in " << dex_file_location_;
     } else {
-      lookup_table_.reset(TypeLookupTable::Open(dex_file_pointer_,
-                                                lookup_table_data_,
-                                                num_class_defs));
+      lookup_table_ = TypeLookupTable::Open(dex_file_pointer_, lookup_table_data_, num_class_defs);
     }
   }
 }
 
+OatFile::OatDexFile::OatDexFile(std::unique_ptr<TypeLookupTable>&& lookup_table)
+    : lookup_table_(std::move(lookup_table)) {}
+
 OatFile::OatDexFile::~OatDexFile() {}
 
 size_t OatFile::OatDexFile::FileSize() const {
@@ -1342,7 +1344,7 @@
   }
   const DexFile::TypeId* type_id = dex_file.FindTypeId(descriptor);
   if (type_id != nullptr) {
-    uint16_t type_idx = dex_file.GetIndexForTypeId(*type_id);
+    dex::TypeIndex type_idx = dex_file.GetIndexForTypeId(*type_id);
     return dex_file.FindClassDef(type_idx);
   }
   return nullptr;
@@ -1540,7 +1542,7 @@
                                         bool* found) {
   DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
-  if (oat_dex_file == nullptr) {
+  if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) {
     *found = false;
     return OatFile::OatClass::Invalid();
   }
@@ -1548,4 +1550,8 @@
   return oat_dex_file->GetOatClass(class_def_idx);
 }
 
+void OatFile::OatDexFile::AssertAotCompiler() {
+  CHECK(Runtime::Current()->IsAotCompiler());
+}
+
 }  // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 63a0e14..29add5b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -384,7 +384,13 @@
   // Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
   std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
 
+  // May return null if the OatDexFile only contains a type lookup table. This case only happens
+  // for the compiler to speed up compilation.
   const OatFile* GetOatFile() const {
+    // Avoid pulling in runtime.h in the header file.
+    if (kIsDebugBuild && oat_file_ == nullptr) {
+      AssertAotCompiler();
+    }
     return oat_file_;
   }
 
@@ -436,6 +442,9 @@
 
   ~OatDexFile();
 
+  // Create only with a type lookup table, used by the compiler to speed up compilation.
+  explicit OatDexFile(std::unique_ptr<TypeLookupTable>&& lookup_table);
+
  private:
   OatDexFile(const OatFile* oat_file,
              const std::string& dex_file_location,
@@ -446,14 +455,16 @@
              const uint32_t* oat_class_offsets_pointer,
              uint8_t* dex_cache_arrays);
 
-  const OatFile* const oat_file_;
+  static void AssertAotCompiler();
+
+  const OatFile* const oat_file_ = nullptr;
   const std::string dex_file_location_;
   const std::string canonical_dex_file_location_;
-  const uint32_t dex_file_location_checksum_;
-  const uint8_t* const dex_file_pointer_;
-  const uint8_t* lookup_table_data_;
-  const uint32_t* const oat_class_offsets_pointer_;
-  uint8_t* const dex_cache_arrays_;
+  const uint32_t dex_file_location_checksum_ = 0u;
+  const uint8_t* const dex_file_pointer_ = nullptr;
+  const uint8_t* lookup_table_data_ = nullptr;
+  const uint32_t* const oat_class_offsets_pointer_ = 0u;
+  uint8_t* const dex_cache_arrays_ = nullptr;
   mutable std::unique_ptr<TypeLookupTable> lookup_table_;
 
   friend class OatFile;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 0679360..4d1e1ea 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,6 +16,8 @@
 
 #include "oat_file_assistant.h"
 
+#include <sstream>
+
 #include <sys/stat.h>
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -34,15 +36,21 @@
 
 std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
   switch (status) {
-    case OatFileAssistant::kOatOutOfDate:
-      stream << "kOatOutOfDate";
+    case OatFileAssistant::kOatCannotOpen:
+      stream << "kOatCannotOpen";
+      break;
+    case OatFileAssistant::kOatDexOutOfDate:
+      stream << "kOatDexOutOfDate";
+      break;
+    case OatFileAssistant::kOatBootImageOutOfDate:
+      stream << "kOatBootImageOutOfDate";
+      break;
+    case OatFileAssistant::kOatRelocationOutOfDate:
+      stream << "kOatRelocationOutOfDate";
       break;
     case OatFileAssistant::kOatUpToDate:
       stream << "kOatUpToDate";
       break;
-    case OatFileAssistant::kOatNeedsRelocation:
-      stream << "kOatNeedsRelocation";
-      break;
     default:
       UNREACHABLE();
   }
@@ -60,7 +68,10 @@
                                    const char* oat_location,
                                    const InstructionSet isa,
                                    bool load_executable)
-    : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
+    : isa_(isa),
+      load_executable_(load_executable),
+      odex_(this, /*is_oat_location*/ false),
+      oat_(this, /*is_oat_location*/ true) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   dex_location_.assign(dex_location);
 
@@ -135,51 +146,13 @@
   return true;
 }
 
-OatFileAssistant::DexOptNeeded
-OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
-  bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
-
-  // See if the oat file is in good shape as is.
-  bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
-  if (oat_okay) {
-    if (compilation_desired) {
-      if (oat_.IsUpToDate()) {
-        return kNoDexOptNeeded;
-      }
-    } else {
-      if (!oat_.IsOutOfDate()) {
-        return kNoDexOptNeeded;
-      }
-    }
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
+  OatFileInfo& info = GetBestInfo();
+  DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed);
+  if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
+    return dexopt_needed;
   }
-
-  // See if the odex file is in good shape as is.
-  bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
-  if (odex_okay) {
-    if (compilation_desired) {
-      if (odex_.IsUpToDate()) {
-        return kNoDexOptNeeded;
-      }
-    } else {
-      if (!odex_.IsOutOfDate()) {
-        return kNoDexOptNeeded;
-      }
-    }
-  }
-
-  // See if we can get an up-to-date file by running patchoat.
-  if (compilation_desired) {
-    if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
-      return kPatchOatNeeded;
-    }
-
-    if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
-      return kSelfPatchOatNeeded;
-    }
-  }
-
-  // We can only run dex2oat if there are original dex files.
-  return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
+  return -dexopt_needed;
 }
 
 // Figure out the currently specified compile filter option in the runtime.
@@ -205,7 +178,7 @@
 }
 
 bool OatFileAssistant::IsUpToDate() {
-  return OatFileIsUpToDate() || OdexFileIsUpToDate();
+  return GetBestInfo().Status() == kOatUpToDate;
 }
 
 OatFileAssistant::ResultOfAttemptToUpdate
@@ -215,59 +188,66 @@
     return kUpdateNotAttempted;
   }
 
-  switch (GetDexOptNeeded(target, profile_changed)) {
-    case kNoDexOptNeeded: return kUpdateSucceeded;
-    case kDex2OatNeeded: return GenerateOatFile(error_msg);
-    case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
-    case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
+  OatFileInfo& info = GetBestInfo();
+  switch (info.GetDexOptNeeded(target, profile_changed)) {
+    case kNoDexOptNeeded:
+      return kUpdateSucceeded;
+
+    // TODO: For now, don't bother with all the different ways we can call
+    // dex2oat to generate the oat file. Always generate the oat file as if it
+    // were kDex2OatFromScratch.
+    case kDex2OatFromScratch:
+    case kDex2OatForBootImage:
+    case kDex2OatForRelocation:
+    case kDex2OatForFilter:
+      return GenerateOatFile(error_msg);
+
+    case kPatchoatForRelocation: {
+      return RelocateOatFile(info.Filename(), error_msg);
+    }
   }
   UNREACHABLE();
 }
 
 std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
-  // The best oat files are, in descending order of bestness:
-  // 1. Properly relocated files. These may be opened executable.
-  // 2. Not out-of-date files that are already opened non-executable.
-  // 3. Not out-of-date files that we must reopen non-executable.
+  return GetBestInfo().ReleaseFileForUse();
+}
 
-  if (oat_.IsUpToDate()) {
-    return oat_.ReleaseFile();
+std::string OatFileAssistant::GetStatusDump() {
+  std::ostringstream status;
+  bool oat_file_exists = false;
+  bool odex_file_exists = false;
+  if (oat_.Status() != kOatCannotOpen) {
+    // If we can open the file, neither Filename nor GetFile should return null.
+    CHECK(oat_.Filename() != nullptr);
+    CHECK(oat_.GetFile() != nullptr);
+
+    oat_file_exists = true;
+    status << *oat_.Filename() << " [compilation_filter=";
+    status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter());
+    status << ", status=" << oat_.Status();
   }
 
-  if (odex_.IsUpToDate()) {
-    return odex_.ReleaseFile();
-  }
+  if (odex_.Status() != kOatCannotOpen) {
+    // If we can open the file, neither Filename nor GetFile should return null.
+    CHECK(odex_.Filename() != nullptr);
+    CHECK(odex_.GetFile() != nullptr);
 
-  VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
-    << " attempting to fall back to interpreting oat file instead.";
-
-  if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
-    return oat_.ReleaseFile();
-  }
-
-  if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
-    return odex_.ReleaseFile();
-  }
-
-  if (!oat_.IsOutOfDate()) {
-    load_executable_ = false;
-    oat_.Reset();
-    if (!oat_.IsOutOfDate()) {
-      CHECK(!oat_.IsExecutable());
-      return oat_.ReleaseFile();
+    odex_file_exists = true;
+    if (oat_file_exists) {
+      status << "] ";
     }
+    status << *odex_.Filename() << " [compilation_filter=";
+    status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter());
+    status << ", status=" << odex_.Status();
   }
 
-  if (!odex_.IsOutOfDate()) {
-    load_executable_ = false;
-    odex_.Reset();
-    if (!odex_.IsOutOfDate()) {
-      CHECK(!odex_.IsExecutable());
-      return odex_.ReleaseFile();
-    }
+  if (!oat_file_exists && !odex_file_exists) {
+    status << "invalid[";
   }
 
-  return std::unique_ptr<OatFile>();
+  status << "]";
+  return status.str();
 }
 
 std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
@@ -317,62 +297,14 @@
   return has_original_dex_files_;
 }
 
-const std::string* OatFileAssistant::OdexFileName() {
-  return odex_.Filename();
-}
-
-bool OatFileAssistant::OdexFileExists() {
-  return odex_.Exists();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
   return odex_.Status();
 }
 
-bool OatFileAssistant::OdexFileIsOutOfDate() {
-  return odex_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OdexFileNeedsRelocation() {
-  return odex_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OdexFileIsUpToDate() {
-  return odex_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
-  return odex_.CompilerFilter();
-}
-
-const std::string* OatFileAssistant::OatFileName() {
-  return oat_.Filename();
-}
-
-bool OatFileAssistant::OatFileExists() {
-  return oat_.Exists();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
   return oat_.Status();
 }
 
-bool OatFileAssistant::OatFileIsOutOfDate() {
-  return oat_.IsOutOfDate();
-}
-
-bool OatFileAssistant::OatFileNeedsRelocation() {
-  return oat_.NeedsRelocation();
-}
-
-bool OatFileAssistant::OatFileIsUpToDate() {
-  return oat_.IsUpToDate();
-}
-
-CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
-  return oat_.CompilerFilter();
-}
-
 OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
   // Verify the dex checksum.
   // Note: GetOatDexFile will return null if the dex checksum doesn't match
@@ -383,7 +315,7 @@
       dex_location_.c_str(), dex_checksum_pointer, &error_msg);
   if (oat_dex_file == nullptr) {
     VLOG(oat) << error_msg;
-    return kOatOutOfDate;
+    return kOatDexOutOfDate;
   }
 
   // Verify the dex checksums for any secondary multidex files
@@ -406,7 +338,7 @@
           << secondary_dex_location
           << ". Expected: " << expected_secondary_checksum
           << ", Actual: " << actual_secondary_checksum;
-        return kOatOutOfDate;
+        return kOatDexOutOfDate;
       }
     } else {
       // If we can't get the checksum for the secondary location, we assume
@@ -425,7 +357,7 @@
       VLOG(oat) << "No image for oat image checksum to match against.";
 
       if (HasOriginalDexFiles()) {
-        return kOatOutOfDate;
+        return kOatBootImageOutOfDate;
       }
 
       // If there is no original dex file to fall back to, grudgingly accept
@@ -439,7 +371,7 @@
     } else if (file.GetOatHeader().GetImageFileLocationOatChecksum()
         != GetCombinedImageChecksum()) {
       VLOG(oat) << "Oat image checksum does not match image checksum.";
-      return kOatOutOfDate;
+      return kOatBootImageOutOfDate;
     }
   } else {
     VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
@@ -450,7 +382,7 @@
       const ImageInfo* image_info = GetImageInfo();
       if (image_info == nullptr) {
         VLOG(oat) << "No image to check oat relocation against.";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
 
       // Verify the oat_data_begin recorded for the image in the oat file matches
@@ -462,7 +394,7 @@
           ": Oat file image oat_data_begin (" << oat_data_begin << ")"
           << " does not match actual image oat_data_begin ("
           << image_info->oat_data_begin << ")";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
 
       // Verify the oat_patch_delta recorded for the image in the oat file matches
@@ -473,7 +405,7 @@
           ": Oat file image patch delta (" << oat_patch_delta << ")"
           << " does not match actual image patch delta ("
           << image_info->patch_delta << ")";
-        return kOatNeedsRelocation;
+        return kOatRelocationOutOfDate;
       }
     } else {
       // Oat files compiled in PIC mode do not require relocation.
@@ -841,6 +773,11 @@
   return combined_image_checksum_;
 }
 
+OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
+  bool use_oat = oat_.IsUseable() || odex_.Status() == kOatCannotOpen;
+  return use_oat ? oat_ : odex_;
+}
+
 std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
   DCHECK(oat_file != nullptr);
   std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art");
@@ -857,16 +794,29 @@
   return ret;
 }
 
-OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
-  : oat_file_assistant_(oat_file_assistant)
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant,
+                                           bool is_oat_location)
+  : oat_file_assistant_(oat_file_assistant), is_oat_location_(is_oat_location)
 {}
 
+bool OatFileAssistant::OatFileInfo::IsOatLocation() {
+  return is_oat_location_;
+}
+
 const std::string* OatFileAssistant::OatFileInfo::Filename() {
   return filename_provided_ ? &filename_ : nullptr;
 }
 
-bool OatFileAssistant::OatFileInfo::Exists() {
-  return GetFile() != nullptr;
+bool OatFileAssistant::OatFileInfo::IsUseable() {
+  switch (Status()) {
+    case kOatCannotOpen:
+    case kOatDexOutOfDate:
+    case kOatBootImageOutOfDate: return false;
+
+    case kOatRelocationOutOfDate:
+    case kOatUpToDate: return true;
+  }
+  UNREACHABLE();
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
@@ -874,7 +824,7 @@
     status_attempted_ = true;
     const OatFile* file = GetFile();
     if (file == nullptr) {
-      status_ = kOatOutOfDate;
+      status_ = kOatCannotOpen;
     } else {
       status_ = oat_file_assistant_->GivenOatFileStatus(*file);
       VLOG(oat) << file->GetLocation() << " is " << status_
@@ -884,22 +834,46 @@
   return status_;
 }
 
-bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
-  return Status() == kOatOutOfDate;
-}
+OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
+    CompilerFilter::Filter target, bool profile_changed) {
+  bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
+  bool filter_okay = CompilerFilterIsOkay(target, profile_changed);
 
-bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
-  return Status() == kOatNeedsRelocation;
-}
+  if (filter_okay && Status() == kOatUpToDate) {
+    // The oat file is in good shape as is.
+    return kNoDexOptNeeded;
+  }
 
-bool OatFileAssistant::OatFileInfo::IsUpToDate() {
-  return Status() == kOatUpToDate;
-}
+  if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) {
+    // If no compilation is desired, then it doesn't matter if the oat
+    // file needs relocation. It's in good shape as is.
+    return kNoDexOptNeeded;
+  }
 
-CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
-  const OatFile* file = GetFile();
-  CHECK(file != nullptr);
-  return file->GetCompilerFilter();
+  if (filter_okay && Status() == kOatRelocationOutOfDate && HasPatchInfo()) {
+    return kPatchoatForRelocation;
+  }
+
+  if (oat_file_assistant_->HasOriginalDexFiles()) {
+    // Run dex2oat for relocation if we didn't have the patch info necessary
+    // to use patchoat.
+    if (filter_okay && Status() == kOatRelocationOutOfDate) {
+      return kDex2OatForRelocation;
+    }
+
+    if (IsUseable()) {
+      return kDex2OatForFilter;
+    }
+
+    if (Status() == kOatBootImageOutOfDate) {
+      return kDex2OatForBootImage;
+    }
+
+    return kDex2OatFromScratch;
+  }
+
+  // Otherwise there is nothing we can do, even if we want to.
+  return kNoDexOptNeeded;
 }
 
 const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
@@ -967,5 +941,31 @@
   return std::move(file_);
 }
 
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
+  if (Status() == kOatUpToDate) {
+    return ReleaseFile();
+  }
+
+  VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+    << " attempting to fall back to interpreting oat file instead.";
+
+  if (Status() == kOatRelocationOutOfDate && !IsExecutable()) {
+    return ReleaseFile();
+  }
+
+  if (Status() == kOatRelocationOutOfDate) {
+    // We are loading an oat file for runtime use that needs relocation.
+    // Reload the file non-executable to ensure that we interpret out of the
+    // dex code in the oat file rather than trying to execute the unrelocated
+    // compiled code.
+    oat_file_assistant_->load_executable_ = false;
+    Reset();
+    if (IsUseable()) {
+      CHECK(!IsExecutable());
+      return ReleaseFile();
+    }
+  }
+  return std::unique_ptr<OatFile>();
+}
 }  // namespace art
 
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 3f018dc..bed1edc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -48,41 +48,56 @@
 class OatFileAssistant {
  public:
   enum DexOptNeeded {
-    // kNoDexOptNeeded - The code for this dex location is up to date and can
-    // be used as is.
+    // No dexopt should (or can) be done to update the apk/jar.
     // Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
     kNoDexOptNeeded = 0,
 
-    // kDex2OatNeeded - In order to make the code for this dex location up to
-    // date, dex2oat must be run on the dex file.
-    // Matches Java: dalvik.system.DexFile.DEX2OAT_NEEDED = 1
-    kDex2OatNeeded = 1,
+    // dex2oat should be run to update the apk/jar from scratch.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FROM_SCRATCH = 1
+    kDex2OatFromScratch = 1,
 
-    // kPatchOatNeeded - In order to make the code for this dex location up to
-    // date, patchoat must be run on the odex file.
-    // Matches Java: dalvik.system.DexFile.PATCHOAT_NEEDED = 2
-    kPatchOatNeeded = 2,
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is out of date with respect to the boot image.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_BOOT_IMAGE
+    kDex2OatForBootImage = 2,
 
-    // kSelfPatchOatNeeded - In order to make the code for this dex location
-    // up to date, patchoat must be run on the oat file.
-    // Matches Java: dalvik.system.DexFile.SELF_PATCHOAT_NEEDED = 3
-    kSelfPatchOatNeeded = 3,
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is out of date with respect to the target compiler filter.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_FILTER
+    kDex2OatForFilter = 3,
+
+    // dex2oat should be run to update the apk/jar because the existing code
+    // is not relocated to match the boot image and does not have the
+    // necessary patch information to use patchoat.
+    // Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_RELOCATION
+    kDex2OatForRelocation = 4,
+
+    // patchoat should be run to update the apk/jar.
+    // Matches Java: dalvik.system.DexFile.PATCHOAT_FOR_RELOCATION
+    kPatchoatForRelocation = 5,
   };
 
   enum OatStatus {
-    // kOatOutOfDate - An oat file is said to be out of date if the file does
-    // not exist, is out of date with respect to the dex file or boot image,
-    // or does not meet the target compilation type.
-    kOatOutOfDate,
+    // kOatCannotOpen - The oat file cannot be opened, because it does not
+    // exist, is unreadable, or otherwise corrupted.
+    kOatCannotOpen,
 
-    // kOatNeedsRelocation - An oat file is said to need relocation if the
-    // code is up to date, but not yet properly relocated for address space
-    // layout randomization (ASLR). In this case, the oat file is neither
-    // "out of date" nor "up to date".
-    kOatNeedsRelocation,
+    // kOatDexOutOfDate - The oat file is out of date with respect to the dex file.
+    kOatDexOutOfDate,
 
-    // kOatUpToDate - An oat file is said to be up to date if it is not out of
-    // date and has been properly relocated for the purposes of ASLR.
+    // kOatBootImageOutOfDate - The oat file is up to date with respect to the
+    // dex file, but is out of date with respect to the boot image.
+    kOatBootImageOutOfDate,
+
+    // kOatRelocationOutOfDate - The oat file is up to date with respect to
+    // the dex file and boot image, but contains compiled code that has the
+    // wrong patch delta with respect to the boot image. Patchoat should be
+    // run on the oat file to update the patch delta of the compiled code to
+    // match the boot image.
+    kOatRelocationOutOfDate,
+
+    // kOatUpToDate - The oat file is completely up to date with respect to
+    // the dex file and boot image.
     kOatUpToDate,
   };
 
@@ -142,8 +157,10 @@
   // dex location that is at least as good as an oat file generated with the
   // given compiler filter. profile_changed should be true to indicate the
   // profile has recently changed for this dex location.
-  DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
-                               bool profile_changed = false);
+  // Returns a positive status code if the status refers to the oat file in
+  // the oat location. Returns a negative status code if the status refers to
+  // the oat file in the odex location.
+  int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false);
 
   // Returns true if there is up-to-date code for this dex location,
   // irrespective of the compiler filter of the up-to-date code.
@@ -179,6 +196,10 @@
   // the OatFileAssistant object.
   std::unique_ptr<OatFile> GetBestOatFile();
 
+  // Returns a human readable description of the status of the code for the
+  // dex file. The returned description is for debugging purposes only.
+  std::string GetStatusDump();
+
   // Open and returns an image space associated with the oat file.
   static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
 
@@ -205,43 +226,16 @@
   // really an oat file. The odex file will often, but not always, have a
   // patch delta of 0 and need to be relocated before use for the purposes of
   // ASLR. The odex file is treated as if it were read-only.
-  // These methods return the location and status of the odex file for the dex
-  // location.
-  // Notes:
-  //  * OdexFileName may return null if the odex file name could not be
-  //    determined.
-  const std::string* OdexFileName();
-  bool OdexFileExists();
+  //
+  // Returns the status of the odex file for the dex location.
   OatStatus OdexFileStatus();
-  bool OdexFileIsOutOfDate();
-  bool OdexFileNeedsRelocation();
-  bool OdexFileIsUpToDate();
-  // Must only be called if the associated odex file exists, i.e, if
-  // |OdexFileExists() == true|.
-  CompilerFilter::Filter OdexFileCompilerFilter();
 
   // When the dex files is compiled on the target device, the oat file is the
   // result. The oat file will have been relocated to some
   // (possibly-out-of-date) offset for ASLR.
-  // These methods return the location and status of the target oat file for
-  // the dex location.
   //
-  // Notes:
-  //  * OatFileName may return null if the oat file name could not be
-  //    determined.
-  const std::string* OatFileName();
-  bool OatFileExists();
+  // Returns the status of the oat file for the dex location.
   OatStatus OatFileStatus();
-  bool OatFileIsOutOfDate();
-  bool OatFileNeedsRelocation();
-  bool OatFileIsUpToDate();
-  // Must only be called if the associated oat file exists, i.e, if
-  // |OatFileExists() == true|.
-  CompilerFilter::Filter OatFileCompilerFilter();
-
-  // Return the status for a given opened oat file with respect to the dex
-  // location.
-  OatStatus GivenOatFileStatus(const OatFile& file);
 
   // Generates the oat file by relocation from the named input file.
   // This does not check the current status before attempting to relocate the
@@ -311,29 +305,39 @@
     // Initially the info is for no file in particular. It will treat the
     // file as out of date until Reset is called with a real filename to use
     // the cache for.
-    explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+    // Pass true for is_oat_location if the information associated with this
+    // OatFileInfo is for the oat location, as opposed to the odex location.
+    OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location);
+
+    bool IsOatLocation();
 
     const std::string* Filename();
-    bool Exists();
+
+    // Returns true if this oat file can be used for running code. The oat
+    // file can be used for running code as long as it is not out of date with
+    // respect to the dex code or boot image. An oat file that is out of date
+    // with respect to relocation is considered useable, because it's possible
+    // to interpret the dex code rather than run the unrelocated compiled
+    // code.
+    bool IsUseable();
+
+    // Returns the status of this oat file.
     OatStatus Status();
-    bool IsOutOfDate();
-    bool NeedsRelocation();
-    bool IsUpToDate();
-    // Must only be called if the associated file exists, i.e, if
-    // |Exists() == true|.
-    CompilerFilter::Filter CompilerFilter();
+
+    // Return the DexOptNeeded value for this oat file with respect to the
+    // given target_compilation_filter.
+    // profile_changed should be true to indicate the profile has recently
+    // changed for this dex location.
+    // If patchoat is needed, this function will return the kPatchOatNeeded
+    // status, not the kSelfPatchOatNeeded status.
+    DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+                                 bool profile_changed);
 
     // Returns the loaded file.
     // Loads the file if needed. Returns null if the file failed to load.
     // The caller shouldn't clean up or free the returned pointer.
     const OatFile* GetFile();
 
-    // Returns true if the compiler filter used to generate the file is at
-    // least as good as the given target filter. profile_changed should be
-    // true to indicate the profile has recently changed for this dex
-    // location.
-    bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
     // Returns true if the file is opened executable.
     bool IsExecutable();
 
@@ -348,6 +352,23 @@
     // file with the given filename.
     void Reset(const std::string& filename);
 
+    // Release the loaded oat file for runtime use.
+    // Returns null if the oat file hasn't been loaded or is out of date.
+    // Ensures the returned file is not loaded executable if it has unuseable
+    // compiled code.
+    //
+    // After this call, no other methods of the OatFileInfo should be
+    // called, because access to the loaded oat file has been taken away from
+    // the OatFileInfo object.
+    std::unique_ptr<OatFile> ReleaseFileForUse();
+
+   private:
+    // Returns true if the compiler filter used to generate the file is at
+    // least as good as the given target filter. profile_changed should be
+    // true to indicate the profile has recently changed for this dex
+    // location.
+    bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
     // Release the loaded oat file.
     // Returns null if the oat file hasn't been loaded.
     //
@@ -356,8 +377,8 @@
     // the OatFileInfo object.
     std::unique_ptr<OatFile> ReleaseFile();
 
-   private:
     OatFileAssistant* oat_file_assistant_;
+    const bool is_oat_location_;
 
     bool filename_provided_ = false;
     std::string filename_;
@@ -374,6 +395,13 @@
     bool file_released_ = false;
   };
 
+  // Return info for the best oat file.
+  OatFileInfo& GetBestInfo();
+
+  // Return the status for a given opened oat file with respect to the dex
+  // location.
+  OatStatus GivenOatFileStatus(const OatFile& file);
+
   // Returns the current image location.
   // Returns an empty string if the image location could not be retrieved.
   //
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d4337b9..5730cf2 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -49,9 +49,9 @@
   // Pre-Relocate the image to a known non-zero offset so we don't have to
   // deal with the runtime randomly relocating the image by 0 and messing up
   // the expected results of the tests.
-  bool PreRelocateImage(std::string* error_msg) {
+  bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
     std::string image;
-    if (!GetCachedImageFile(&image, error_msg)) {
+    if (!GetCachedImageFile(image_location, &image, error_msg)) {
       return false;
     }
 
@@ -60,7 +60,7 @@
 
     std::vector<std::string> argv;
     argv.push_back(patchoat);
-    argv.push_back("--input-image-location=" + GetImageLocation());
+    argv.push_back("--input-image-location=" + image_location);
     argv.push_back("--output-image-file=" + image);
     argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
     argv.push_back("--base-offset-delta=0x00008000");
@@ -69,8 +69,8 @@
 
   virtual void PreRuntimeCreate() {
     std::string error_msg;
-    ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
-
+    ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+    ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
     UnreserveImageSpace();
   }
 
@@ -78,24 +78,32 @@
     ReserveImageSpace();
   }
 
-  // Generate a non-PIC odex file for the purposes of test.
-  // The generated odex file will be un-relocated.
-  void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location,
-                           CompilerFilter::Filter filter,
-                           bool pic = false,
-                           bool with_patch_info = true) {
-    // Temporarily redirect the dalvik cache so dex2oat doesn't find the
-    // relocated image file.
+  // Generate an oat file for the purposes of test.
+  void GenerateOatForTest(const std::string& dex_location,
+                          const std::string& oat_location,
+                          CompilerFilter::Filter filter,
+                          bool relocate,
+                          bool pic,
+                          bool with_patch_info,
+                          bool with_alternate_image) {
     std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
     std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
-    ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+
+    if (!relocate) {
+      // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+      // relocated image file.
+      ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+    }
 
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
-    args.push_back("--oat-file=" + odex_location);
+    args.push_back("--oat-file=" + oat_location);
     args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
     args.push_back("--runtime-arg");
+
+    // Use -Xnorelocate regardless of the relocate argument.
+    // We control relocation by redirecting the dalvik cache when needed
+    // rather than use this flag.
     args.push_back("-Xnorelocate");
 
     if (pic) {
@@ -106,14 +114,22 @@
       args.push_back("--include-patch-information");
     }
 
+    std::string image_location = GetImageLocation();
+    if (with_alternate_image) {
+      args.push_back("--boot-image=" + GetImageLocation2());
+    }
+
     std::string error_msg;
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-    ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
 
-    // Verify the odex file was generated as expected and really is
-    // unrelocated.
-    std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
-                                                     odex_location.c_str(),
+    if (!relocate) {
+      // Restore the dalvik cache if needed.
+      ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+    }
+
+    // Verify the odex file was generated as expected.
+    std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+                                                     oat_location.c_str(),
                                                      nullptr,
                                                      nullptr,
                                                      false,
@@ -125,24 +141,59 @@
     EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
     EXPECT_EQ(filter, odex_file->GetCompilerFilter());
 
-    if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
-      const std::vector<gc::space::ImageSpace*> image_spaces =
-        Runtime::Current()->GetHeap()->GetBootImageSpaces();
-      ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
-      const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
-      const OatHeader& oat_header = odex_file->GetOatHeader();
-      uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
-      EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
-      EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
-          oat_header.GetImageFileLocationOatDataBegin());
-      EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());
+    std::unique_ptr<ImageHeader> image_header(
+            gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+                                                   kRuntimeISA,
+                                                   &error_msg));
+    ASSERT_TRUE(image_header != nullptr) << error_msg;
+    const OatHeader& oat_header = odex_file->GetOatHeader();
+    uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+    if (CompilerFilter::DependsOnImageChecksum(filter)) {
+      if (with_alternate_image) {
+        EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      } else {
+        EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      }
     }
+
+    if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+      if (relocate) {
+        EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+            oat_header.GetImageFileLocationOatDataBegin());
+        EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+      } else {
+        EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+            oat_header.GetImageFileLocationOatDataBegin());
+        EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+      }
+    }
+  }
+
+  // Generate a non-PIC odex file for the purposes of test.
+  // The generated odex file will be un-relocated.
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter) {
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/false,
+                       /*with_patch_info*/true,
+                       /*with_alternate_image*/false);
   }
 
   void GeneratePicOdexForTest(const std::string& dex_location,
                               const std::string& odex_location,
                               CompilerFilter::Filter filter) {
-    GenerateOdexForTest(dex_location, odex_location, filter, true, false);
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/true,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
   }
 
   // Generate a non-PIC odex file without patch information for the purposes
@@ -150,7 +201,43 @@
   void GenerateNoPatchOdexForTest(const std::string& dex_location,
                                   const std::string& odex_location,
                                   CompilerFilter::Filter filter) {
-    GenerateOdexForTest(dex_location, odex_location, filter, false, false);
+    GenerateOatForTest(dex_location,
+                       odex_location,
+                       filter,
+                       /*relocate*/false,
+                       /*pic*/false,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
+  }
+
+  // Generate an oat file in the oat location.
+  void GenerateOatForTest(const char* dex_location,
+                          CompilerFilter::Filter filter,
+                          bool relocate,
+                          bool pic,
+                          bool with_patch_info,
+                          bool with_alternate_image) {
+    std::string oat_location;
+    std::string error_msg;
+    ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+          dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+    GenerateOatForTest(dex_location,
+                       oat_location,
+                       filter,
+                       relocate,
+                       pic,
+                       with_patch_info,
+                       with_alternate_image);
+  }
+
+  // Generate a standard oat file in the oat location.
+  void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+    GenerateOatForTest(dex_location,
+                       filter,
+                       /*relocate*/true,
+                       /*pic*/false,
+                       /*with_patch_info*/false,
+                       /*with_alternate_image*/false);
   }
 
  private:
@@ -211,36 +298,6 @@
   }
 };
 
-// Generate an oat file for the purposes of test, as opposed to testing
-// generation of oat files.
-static void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
-  // Use an oat file assistant to find the proper oat location.
-  std::string oat_location;
-  std::string error_msg;
-  ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
-        dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
-
-  std::vector<std::string> args;
-  args.push_back("--dex-file=" + std::string(dex_location));
-  args.push_back("--oat-file=" + oat_location);
-  args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
-  args.push_back("--runtime-arg");
-  args.push_back("-Xnorelocate");
-  ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
-  // Verify the oat file was generated as expected.
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location.c_str(),
-                                                  oat_location.c_str(),
-                                                  nullptr,
-                                                  nullptr,
-                                                  false,
-                                                  /*low_4gb*/false,
-                                                  dex_location,
-                                                  &error_msg));
-  ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
-  EXPECT_EQ(filter, oat_file->GetCompilerFilter());
-}
-
 // Case: We have a DEX file, but no OAT file for it.
 // Expect: The status is kDex2OatNeeded.
 TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -249,26 +306,18 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-  EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -307,17 +356,11 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -337,17 +380,12 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // Case: We have a DEX file and speed-profile OAT file for it.
@@ -364,19 +402,13 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, false));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, false));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, true));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly, true));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -416,7 +448,7 @@
   Copy(GetMultiDexSrc2(), dex_location);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed, false));
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
@@ -453,10 +485,10 @@
   EXPECT_EQ(2u, dex_files.size());
 }
 
-// Case: We have a DEX file and out-of-date OAT file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, OatOutOfDate) {
-  std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
 
   // We create a dex, generate an oat for it, then overwrite the dex with a
   // different dex to make the oat out of date.
@@ -465,18 +497,68 @@
   Copy(GetDexSrc2(), dex_location);
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(),
+                     CompilerFilter::kSpeed,
+                     /*relocate*/true,
+                     /*pic*/false,
+                     /*with_patch_info*/false,
+                     /*with_alternate_image*/true);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str(),
+                     CompilerFilter::kVerifyAtRuntime,
+                     /*relocate*/true,
+                     /*pic*/false,
+                     /*with_patch_info*/false,
+                     /*with_alternate_image*/true);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
+      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -495,17 +577,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // We should still be able to get the non-executable odex file to run from.
@@ -529,16 +606,12 @@
   // Verify the status.
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -551,12 +624,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
@@ -590,19 +659,14 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,  // Can't run dex2oat because dex file is stripped.
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -617,14 +681,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
@@ -654,13 +712,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date. This should have no effect.
@@ -673,13 +726,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -699,20 +747,14 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded,
+  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
@@ -725,14 +767,8 @@
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -757,7 +793,7 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
       oat_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   // Make the oat file up to date.
@@ -778,7 +814,7 @@
 
 // Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
 // OAT files both have patch delta of 0.
-// Expect: It shouldn't crash, and status is kPatchOatNeeded.
+// Expect: It shouldn't crash, and status is kSelfPatchOatNeeded.
 TEST_F(OatFileAssistantTest, OdexOatOverlap) {
   std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
   std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -796,16 +832,15 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(),
       oat_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded,
+  // kPatchoatForRelocation is expected rather than -kPatchoatForRelocation
+  // based on the assumption that the oat location is more up-to-date than the odex
+  // location, even if they both need relocation.
+  EXPECT_EQ(OatFileAssistant::kPatchoatForRelocation,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatRelocationOutOfDate, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Things aren't relocated, so it should fall back to interpreted.
@@ -833,16 +868,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -861,16 +892,12 @@
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerifyAtRuntime));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
@@ -958,7 +985,7 @@
 
   // Verify it didn't create an oat in the default location.
   OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
-  EXPECT_FALSE(ofm.OatFileExists());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
 }
 
 // Case: We have a DEX file but can't write the oat file.
@@ -1043,14 +1070,10 @@
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // Case: Very short, non-existent Dex location.
@@ -1063,12 +1086,8 @@
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
   EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Trying to make it up to date should have no effect.
@@ -1087,16 +1106,12 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
 }
 
 // A task to generate a dex location. Used by the RaceToGenerate test.
@@ -1226,7 +1241,7 @@
       oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kInterpretOnly));
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded,
+  EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
 
   Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
@@ -1263,6 +1278,15 @@
 // Verify the dexopt status values from dalvik.system.DexFile
 // match the OatFileAssistant::DexOptStatus values.
 TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+  std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = {
+    {OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"},
+    {OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"},
+    {OatFileAssistant::kDex2OatForBootImage, "DEX2OAT_FOR_BOOT_IMAGE"},
+    {OatFileAssistant::kDex2OatForFilter, "DEX2OAT_FOR_FILTER"},
+    {OatFileAssistant::kDex2OatForRelocation, "DEX2OAT_FOR_RELOCATION"},
+    {OatFileAssistant::kPatchoatForRelocation, "PATCHOAT_FOR_RELOCATION"}
+  };
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
@@ -1271,35 +1295,16 @@
   ASSERT_FALSE(dexfile.Get() == nullptr);
   linker->EnsureInitialized(soa.Self(), dexfile, true, true);
 
-  ArtField* no_dexopt_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "NO_DEXOPT_NEEDED", "I");
-  ASSERT_FALSE(no_dexopt_needed == nullptr);
-  EXPECT_EQ(no_dexopt_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, no_dexopt_needed->GetInt(dexfile.Get()));
-
-  ArtField* dex2oat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "DEX2OAT_NEEDED", "I");
-  ASSERT_FALSE(dex2oat_needed == nullptr);
-  EXPECT_EQ(dex2oat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, dex2oat_needed->GetInt(dexfile.Get()));
-
-  ArtField* patchoat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "PATCHOAT_NEEDED", "I");
-  ASSERT_FALSE(patchoat_needed == nullptr);
-  EXPECT_EQ(patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, patchoat_needed->GetInt(dexfile.Get()));
-
-  ArtField* self_patchoat_needed = mirror::Class::FindStaticField(
-      soa.Self(), dexfile, "SELF_PATCHOAT_NEEDED", "I");
-  ASSERT_FALSE(self_patchoat_needed == nullptr);
-  EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
-  EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+  for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) {
+    ArtField* art_field = mirror::Class::FindStaticField(
+        soa.Self(), dexfile, field.second, "I");
+    ASSERT_FALSE(art_field == nullptr);
+    EXPECT_EQ(art_field->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+    EXPECT_EQ(field.first, art_field->GetInt(dexfile.Get()));
+  }
 }
 
 // TODO: More Tests:
-//  * Image checksum change is out of date for kIntepretOnly, but not
-//    kVerifyAtRuntime. But target of kVerifyAtRuntime still says current
-//    kInterpretOnly is out of date.
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
 //  * Test using secondary isa
@@ -1313,5 +1318,4 @@
 //    because it's unrelocated and no dex2oat
 //  * Test unrelocated specific target compilation type can be relocated to
 //    make it up to date.
-
 }  // namespace art
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index ee5002f..4afca7d 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -67,6 +67,11 @@
     return data;
   }
 
+  uint8_t* GetOptimizedCodeInfoPtr() {
+    DCHECK(IsOptimized());
+    return code_ - vmap_table_offset_;
+  }
+
   CodeInfo GetOptimizedCodeInfo() const {
     return CodeInfo(GetOptimizedCodeInfoPtr());
   }
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 0eff469..5e588a8 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -269,6 +269,12 @@
       ReportRoot(root_obj, info);
     }
 
+    // Remove NO_THREAD_SAFETY_ANALYSIS once ASSERT_CAPABILITY works correctly.
+    art::Thread* FindThread(const art::RootInfo& info) NO_THREAD_SAFETY_ANALYSIS {
+      art::Locks::thread_list_lock_->AssertExclusiveHeld(art::Thread::Current());
+      return art::Runtime::Current()->GetThreadList()->FindThreadByThreadId(info.GetThreadId());
+    }
+
     jvmtiHeapReferenceKind GetReferenceKind(const art::RootInfo& info,
                                             jvmtiHeapReferenceInfo* ref_info)
         REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -280,7 +286,34 @@
           return JVMTI_HEAP_REFERENCE_JNI_GLOBAL;
 
         case art::RootType::kRootJNILocal:
+        {
+          uint32_t thread_id = info.GetThreadId();
+          ref_info->jni_local.thread_id = thread_id;
+
+          art::Thread* thread = FindThread(info);
+          if (thread != nullptr) {
+            art::mirror::Object* thread_obj = thread->GetPeer();
+            if (thread->IsStillStarting()) {
+              thread_obj = nullptr;
+            } else {
+              thread_obj = thread->GetPeer();
+            }
+            if (thread_obj != nullptr) {
+              ref_info->jni_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
+            }
+          }
+
+          // TODO: We don't have this info.
+          if (thread != nullptr) {
+            ref_info->jni_local.depth = 0;
+            art::ArtMethod* method = thread->GetCurrentMethod(nullptr, false /* abort_on_error */);
+            if (method != nullptr) {
+              ref_info->jni_local.method = art::jni::EncodeArtMethod(method);
+            }
+          }
+
           return JVMTI_HEAP_REFERENCE_JNI_LOCAL;
+        }
 
         case art::RootType::kRootJavaFrame:
           return JVMTI_HEAP_REFERENCE_STACK_LOCAL;
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index e391a9d..ffa5ac7 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -78,7 +78,9 @@
   }
 
   // TODO: Support generic signature.
-  *generic_ptr = nullptr;
+  if (generic_ptr != nullptr) {
+    *generic_ptr = nullptr;
+  }
 
   // Everything is fine, release the buffers.
   name_copy.release();
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index fa2983c..7bb5205 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -33,6 +33,7 @@
 
 #include "class_linker.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "gc_root-inl.h"
 #include "globals.h"
 #include "jni_env_ext-inl.h"
@@ -108,10 +109,10 @@
     // 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 =
+    art::dex::TypeIndex 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;
+    std::vector<art::dex::TypeIndex> 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(
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 3128380..8446b52 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -363,7 +363,7 @@
   Thread* const self = Thread::Current();
   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_;
+    dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
     ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
                                                               true /* resolve*/,
                                                               pointer_size));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6177ef9..ee4d669 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -81,6 +81,7 @@
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
+#include "jit/jit_code_cache.h"
 #include "jni_internal.h"
 #include "linear_alloc.h"
 #include "mirror/array.h"
@@ -372,6 +373,7 @@
   void Dump(std::ostream& os) const {
     if (gAborting > 1) {
       os << "Runtime aborting --- recursively, so no thread-specific detail!\n";
+      DumpRecursiveAbort(os);
       return;
     }
     gAborting++;
@@ -428,6 +430,21 @@
       }
     }
   }
+
+  // For recursive aborts.
+  void DumpRecursiveAbort(std::ostream& os) const NO_THREAD_SAFETY_ANALYSIS {
+    // The only thing we'll attempt is dumping the native stack of the current thread. We will only
+    // try this if we haven't exceeded an arbitrary amount of recursions, to recover and actually
+    // die.
+    // Note: as we're using a global counter for the recursive abort detection, there is a potential
+    //       race here and it is not OK to just print when the counter is "2" (one from
+    //       Runtime::Abort(), one from previous Dump() call). Use a number that seems large enough.
+    static constexpr size_t kOnlyPrintWhenRecursionLessThan = 100u;
+    if (gAborting < kOnlyPrintWhenRecursionLessThan) {
+      gAborting++;
+      DumpNativeStack(os, GetTid());
+    }
+  }
 };
 
 void Runtime::Abort(const char* msg) {
@@ -442,8 +459,16 @@
 
   // Many people have difficulty distinguish aborts from crashes,
   // so be explicit.
+  // Note: use cerr on the host to print log lines immediately, so we get at least some output
+  //       in case of recursive aborts. We lose annotation with the source file and line number
+  //       here, which is a minor issue. The same is significantly more complicated on device,
+  //       which is why we ignore the issue there.
   AbortState state;
-  LOG(FATAL_WITHOUT_ABORT) << Dumpable<AbortState>(state);
+  if (kIsTargetBuild) {
+    LOG(FATAL_WITHOUT_ABORT) << Dumpable<AbortState>(state);
+  } else {
+    std::cerr << Dumpable<AbortState>(state);
+  }
 
   // Sometimes we dump long messages, and the Android abort message only retains the first line.
   // In those cases, just log the message again, to avoid logcat limits.
@@ -492,6 +517,14 @@
   GetMonitorList()->SweepMonitorList(visitor);
   GetJavaVM()->SweepJniWeakGlobals(visitor);
   GetHeap()->SweepAllocationRecords(visitor);
+  if (GetJit() != nullptr) {
+    // Visit JIT literal tables. Objects in these tables are classes and strings
+    // and only classes can be affected by class unloading. The strings always
+    // stay alive as they are strongly interned.
+    // TODO: Move this closer to CleanupClassLoaders, to avoid blocking weak accesses
+    // from mutators. See b/32167580.
+    GetJit()->GetCodeCache()->SweepRootTables(visitor);
+  }
 
   // All other generic system-weak holders.
   for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) {
@@ -1029,8 +1062,10 @@
                        runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
                        runtime_options.GetOrDefault(Opt::Image),
                        runtime_options.GetOrDefault(Opt::ImageInstructionSet),
-                       xgc_option.collector_type_,
-                       runtime_options.GetOrDefault(Opt::BackgroundGc),
+                       // Override the collector type to CC if the read barrier config.
+                       kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
+                       kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
+                                       : runtime_options.GetOrDefault(Opt::BackgroundGc),
                        runtime_options.GetOrDefault(Opt::LargeObjectSpace),
                        runtime_options.GetOrDefault(Opt::LargeObjectThreshold),
                        runtime_options.GetOrDefault(Opt::ParallelGCThreads),
@@ -2148,7 +2183,7 @@
 
 NO_RETURN
 void Runtime::Aborter(const char* abort_message) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
   android_set_abort_message(abort_message);
 #endif
   Runtime::Abort(abort_message);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index debd13a..c92e38b 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1906,7 +1906,7 @@
   }
   delete tlsPtr_.instrumentation_stack;
   delete tlsPtr_.name;
-  delete tlsPtr_.stack_trace_sample;
+  delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
   free(tlsPtr_.nested_signal_state);
 
   Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
diff --git a/runtime/thread.h b/runtime/thread.h
index 2079646..35226f2 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -39,6 +39,7 @@
 #include "jvalue.h"
 #include "object_callbacks.h"
 #include "offsets.h"
+#include "runtime.h"
 #include "runtime_stats.h"
 #include "stack.h"
 #include "thread_state.h"
@@ -70,7 +71,8 @@
 }  // namespace mirror
 
 namespace verifier {
-class MethodVerifier;
+  class MethodVerifier;
+  class VerifierDeps;
 }  // namespace verifier
 
 class ArtMethod;
@@ -947,11 +949,27 @@
   }
 
   std::vector<ArtMethod*>* GetStackTraceSample() const {
-    return tlsPtr_.stack_trace_sample;
+    DCHECK(!Runtime::Current()->IsAotCompiler());
+    return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
   }
 
   void SetStackTraceSample(std::vector<ArtMethod*>* sample) {
-    tlsPtr_.stack_trace_sample = sample;
+    DCHECK(!Runtime::Current()->IsAotCompiler());
+    tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample;
+  }
+
+  verifier::VerifierDeps* GetVerifierDeps() const {
+    DCHECK(Runtime::Current()->IsAotCompiler());
+    return tlsPtr_.deps_or_stack_trace_sample.verifier_deps;
+  }
+
+  // It is the responsability of the caller to make sure the verifier_deps
+  // entry in the thread is cleared before destruction of the actual VerifierDeps
+  // object, or the thread.
+  void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) {
+    DCHECK(Runtime::Current()->IsAotCompiler());
+    DCHECK(verifier_deps == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr);
+    tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
   }
 
   uint64_t GetTraceClockBase() const {
@@ -1378,7 +1396,7 @@
       tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr),
       managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr),
       self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0),
-      stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
+      deps_or_stack_trace_sample(), wait_next(nullptr), monitor_enter_object(nullptr),
       top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
       instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
       stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
@@ -1432,8 +1450,18 @@
     // Size of the stack.
     size_t stack_size;
 
-    // Pointer to previous stack trace captured by sampling profiler.
-    std::vector<ArtMethod*>* stack_trace_sample;
+    // Sampling profiler and AOT verification cannot happen on the same run, so we share
+    // the same entry for the stack trace and the verifier deps.
+    union DepsOrStackTraceSample {
+      DepsOrStackTraceSample() {
+        verifier_deps = nullptr;
+        stack_trace_sample = nullptr;
+      }
+      // Pointer to previous stack trace captured by sampling profiler.
+      std::vector<ArtMethod*>* stack_trace_sample;
+      // When doing AOT verification, per-thread VerifierDeps.
+      verifier::VerifierDeps* verifier_deps;
+    } deps_or_stack_trace_sample;
 
     // The next thread in the wait set this thread is part of or null if not waiting.
     Thread* wait_next;
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index b14f340..d9d2ea3 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -85,6 +85,7 @@
   ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg);
   Runtime* runtime = Runtime::Current();
   CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
+  worker->thread_ = Thread::Current();
   // Do work until its time to shut down.
   worker->Run();
   runtime->DetachCurrentThread();
@@ -177,7 +178,7 @@
     }
 
     ++waiting_count_;
-    if (waiting_count_ == GetThreadCount() && tasks_.empty()) {
+    if (waiting_count_ == GetThreadCount() && !HasOutstandingTasks()) {
       // We may be done, lets broadcast to the completion condition.
       completion_condition_.Broadcast(self);
     }
@@ -200,7 +201,7 @@
 }
 
 Task* ThreadPool::TryGetTaskLocked() {
-  if (started_ && !tasks_.empty()) {
+  if (HasOutstandingTasks()) {
     Task* task = tasks_.front();
     tasks_.pop_front();
     return task;
@@ -218,7 +219,7 @@
   }
   // Wait until each thread is waiting and the task list is empty.
   MutexLock mu(self, task_queue_lock_);
-  while (!shutting_down_ && (waiting_count_ != GetThreadCount() || !tasks_.empty())) {
+  while (!shutting_down_ && (waiting_count_ != GetThreadCount() || HasOutstandingTasks())) {
     if (!may_hold_locks) {
       completion_condition_.Wait(self);
     } else {
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index b6c6f02..eaadfe0 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -62,6 +62,8 @@
   // Set the "nice" priorty for this worker.
   void SetPthreadPriority(int priority);
 
+  Thread* GetThread() const { return thread_; }
+
  protected:
   ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size);
   static void* Callback(void* arg) REQUIRES(!Locks::mutator_lock_);
@@ -71,6 +73,7 @@
   const std::string name_;
   std::unique_ptr<MemMap> stack_;
   pthread_t pthread_;
+  Thread* thread_;
 
  private:
   friend class ThreadPool;
@@ -84,6 +87,10 @@
     return threads_.size();
   }
 
+  const std::vector<ThreadPoolWorker*>& GetWorkers() const {
+    return threads_;
+  }
+
   // Broadcast to the workers and tell them to empty out the work queue.
   void StartWorkers(Thread* self) REQUIRES(!task_queue_lock_);
 
@@ -100,7 +107,8 @@
   ThreadPool(const char* name, size_t num_threads);
   virtual ~ThreadPool();
 
-  // Wait for all tasks currently on queue to get completed.
+  // Wait for all tasks currently on queue to get completed. If the pool has been stopped, only
+  // wait till all already running tasks are done.
   void Wait(Thread* self, bool do_work, bool may_hold_locks) REQUIRES(!task_queue_lock_);
 
   size_t GetTaskCount(Thread* self) REQUIRES(!task_queue_lock_);
@@ -130,6 +138,10 @@
     return shutting_down_;
   }
 
+  bool HasOutstandingTasks() const REQUIRES(task_queue_lock_) {
+    return started_ && !tasks_.empty();
+  }
+
   const std::string name_;
   Mutex task_queue_lock_;
   ConditionVariable task_queue_condition_ GUARDED_BY(task_queue_lock_);
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index d5f17d1..14c2c3b 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -98,6 +98,29 @@
   thread_pool.Wait(self, false, false);
 }
 
+TEST_F(ThreadPoolTest, StopWait) {
+  Thread* self = Thread::Current();
+  ThreadPool thread_pool("Thread pool test thread pool", num_threads);
+
+  AtomicInteger count(0);
+  static const int32_t num_tasks = num_threads * 100;
+  for (int32_t i = 0; i < num_tasks; ++i) {
+    thread_pool.AddTask(self, new CountTask(&count));
+  }
+
+  // Signal the threads to start processing tasks.
+  thread_pool.StartWorkers(self);
+  usleep(200);
+  thread_pool.StopWorkers(self);
+
+  thread_pool.Wait(self, false, false);  // We should not deadlock here.
+
+  // Drain the task list. Note: we have to restart here, as no tasks will be finished when
+  // the pool is stopped.
+  thread_pool.StartWorkers(self);
+  thread_pool.Wait(self, /* do_work */ true, false);
+}
+
 class TreeTask : public Task {
  public:
   TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth)
diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc
index 56e9262..16cd722 100644
--- a/runtime/type_lookup_table.cc
+++ b/runtime/type_lookup_table.cc
@@ -50,17 +50,19 @@
   return num_class_defs != 0u && num_class_defs <= std::numeric_limits<uint16_t>::max();
 }
 
-TypeLookupTable* TypeLookupTable::Create(const DexFile& dex_file, uint8_t* storage) {
+std::unique_ptr<TypeLookupTable> TypeLookupTable::Create(const DexFile& dex_file,
+                                                         uint8_t* storage) {
   const uint32_t num_class_defs = dex_file.NumClassDefs();
-  return SupportedSize(num_class_defs)
+  return std::unique_ptr<TypeLookupTable>(SupportedSize(num_class_defs)
       ? new TypeLookupTable(dex_file, storage)
-      : nullptr;
+      : nullptr);
 }
 
-TypeLookupTable* TypeLookupTable::Open(const uint8_t* dex_file_pointer,
-                                       const uint8_t* raw_data,
-                                       uint32_t num_class_defs) {
-  return new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs);
+std::unique_ptr<TypeLookupTable> TypeLookupTable::Open(const uint8_t* dex_file_pointer,
+                                                       const uint8_t* raw_data,
+                                                       uint32_t num_class_defs) {
+  return std::unique_ptr<TypeLookupTable>(
+      new TypeLookupTable(dex_file_pointer, raw_data, num_class_defs));
 }
 
 TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage)
diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h
index 9595743..3f6f76f 100644
--- a/runtime/type_lookup_table.h
+++ b/runtime/type_lookup_table.h
@@ -60,13 +60,14 @@
   }
 
   // Method creates lookup table for dex file
-  static TypeLookupTable* Create(const DexFile& dex_file, uint8_t* storage = nullptr);
+  static std::unique_ptr<TypeLookupTable> Create(const DexFile& dex_file,
+                                                 uint8_t* storage = nullptr);
 
   // Method opens lookup table from binary data. Lookups will traverse strings and other
   // data contained in dex_file as well.  Lookup table does not own raw_data or dex_file.
-  static TypeLookupTable* Open(const uint8_t* dex_file_pointer,
-                               const uint8_t* raw_data,
-                               uint32_t num_class_defs);
+  static std::unique_ptr<TypeLookupTable> Open(const uint8_t* dex_file_pointer,
+                                               const uint8_t* raw_data,
+                                               uint32_t num_class_defs);
 
   // Method returns pointer to binary data of lookup table. Used by the oat writer.
   const uint8_t* RawData() const {
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index c7875b5..bd1b044 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -65,8 +65,8 @@
   return PointerSize::k32;
 }
 
-inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
-  return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx);
+inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const {
+  return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_);
 }
 
 inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index ae3bfab..7d4b23a 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
 
 #include "dex_file.h"
+#include "dex_file_types.h"
 
 namespace art {
 
@@ -59,7 +60,7 @@
     return types_offset_;
   }
 
-  size_t TypeOffset(uint32_t type_idx) const;
+  size_t TypeOffset(dex::TypeIndex type_idx) const;
 
   size_t TypesSize(size_t num_elements) const;
 
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index b3dab58..843be92 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -20,6 +20,7 @@
 
 #include "base/logging.h"
 #include "base/unix_file/fd_file.h"
+#include "dex_file.h"
 
 namespace art {
 
@@ -73,10 +74,19 @@
     return nullptr;
   }
 
+  return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+}
+
+VdexFile* VdexFile::Open(int file_fd,
+                         size_t vdex_length,
+                         const std::string& vdex_filename,
+                         bool writable,
+                         bool low_4gb,
+                         std::string* error_msg) {
   std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
                                                writable ? PROT_READ | PROT_WRITE : PROT_READ,
                                                MAP_SHARED,
-                                               vdex_file->Fd(),
+                                               file_fd,
                                                0 /* start offset */,
                                                low_4gb,
                                                vdex_filename.c_str(),
@@ -90,4 +100,16 @@
   return new VdexFile(mmap.release());
 }
 
+const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const {
+  DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End()));
+  if (cursor == nullptr) {
+    // Beginning of the iteration, return the first dex file if there is one.
+    return HasDexSection() ? DexBegin() : nullptr;
+  } else {
+    // Fetch the next dex file. Return null if there is none.
+    const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_;
+    return (data == DexEnd()) ? nullptr : data;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index edd6ffe..75a0d5e 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -71,6 +71,13 @@
                         bool low_4gb,
                         std::string* error_msg);
 
+  static VdexFile* Open(int file_fd,
+                        size_t vdex_length,
+                        const std::string& vdex_filename,
+                        bool writable,
+                        bool low_4gb,
+                        std::string* error_msg);
+
   const uint8_t* Begin() const { return mmap_->Begin(); }
   const uint8_t* End() const { return mmap_->End(); }
   size_t Size() const { return mmap_->Size(); }
@@ -84,9 +91,37 @@
         Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
   }
 
+  ArrayRef<const uint8_t> GetQuickeningInfo() const {
+    return ArrayRef<const uint8_t>(
+        GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(),
+        GetHeader().GetQuickeningInfoSize());
+  }
+
+  bool IsValid() const {
+    return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid();
+  }
+
+  // This method is for iterating over the dex files in the vdex. If `cursor` is null,
+  // the first dex file is returned. If `cursor` is not null, it must point to a dex
+  // file and this method returns the next dex file if there is one, or null if there
+  // is none.
+  const uint8_t* GetNextDexFileData(const uint8_t* cursor) const;
+
  private:
   explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
 
+  bool HasDexSection() const {
+    return GetHeader().GetDexSize() != 0;
+  }
+
+  const uint8_t* DexBegin() const {
+    return Begin() + sizeof(Header);
+  }
+
+  const uint8_t* DexEnd() const {
+    return Begin() + sizeof(Header) + GetHeader().GetDexSize();
+  }
+
   std::unique_ptr<MemMap> mmap_;
 
   DISALLOW_COPY_AND_ASSIGN(VdexFile);
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index def61db..363bd8f 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -74,7 +74,7 @@
   return !failure_messages_.empty();
 }
 
-inline const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
+inline const RegType& MethodVerifier::ResolveCheckedClass(dex::TypeIndex class_idx) {
   DCHECK(!HasFailures());
   const RegType& result = ResolveClassAndCheckAccess(class_idx);
   DCHECK(!HasFailures());
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index d9e3ea7..7137db8 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -100,8 +100,18 @@
 ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition,
                                              const char* error_msg, uint32_t work_insn_idx) {
   if (kIsDebugBuild) {
-    // In a debug build, abort if the error condition is wrong.
-    DCHECK(condition) << error_msg << work_insn_idx;
+    // In a debug build, abort if the error condition is wrong. Only warn if
+    // we are already aborting (as this verification is likely run to print
+    // lock information).
+    if (LIKELY(gAborting == 0)) {
+      DCHECK(condition) << error_msg << work_insn_idx;
+    } else {
+      if (!condition) {
+        LOG(ERROR) << error_msg << work_insn_idx;
+        verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
+        return true;
+      }
+    }
   } else {
     // In a non-debug build, just fail the class.
     if (!condition) {
@@ -1062,7 +1072,7 @@
       GetInstructionFlags(dex_pc).SetBranchTarget();
       // Ensure exception types are resolved so that they don't need resolution to be delivered,
       // unresolved exception types will be ignored by exception delivery
-      if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) {
+      if (iterator.GetHandlerTypeIndex().IsValid()) {
         mirror::Class* exception_type = linker->ResolveType(*dex_file_,
                                                             iterator.GetHandlerTypeIndex(),
                                                             dex_cache_, class_loader_);
@@ -1145,13 +1155,13 @@
       result = result && CheckMethodIndex(inst->VRegB());
       break;
     case Instruction::kVerifyRegBNewInstance:
-      result = result && CheckNewInstance(inst->VRegB());
+      result = result && CheckNewInstance(dex::TypeIndex(inst->VRegB()));
       break;
     case Instruction::kVerifyRegBString:
       result = result && CheckStringIndex(inst->VRegB());
       break;
     case Instruction::kVerifyRegBType:
-      result = result && CheckTypeIndex(inst->VRegB());
+      result = result && CheckTypeIndex(dex::TypeIndex(inst->VRegB()));
       break;
     case Instruction::kVerifyRegBWide:
       result = result && CheckWideRegisterIndex(inst->VRegB());
@@ -1165,10 +1175,10 @@
       result = result && CheckFieldIndex(inst->VRegC());
       break;
     case Instruction::kVerifyRegCNewArray:
-      result = result && CheckNewArray(inst->VRegC());
+      result = result && CheckNewArray(dex::TypeIndex(inst->VRegC()));
       break;
     case Instruction::kVerifyRegCType:
-      result = result && CheckTypeIndex(inst->VRegC());
+      result = result && CheckTypeIndex(dex::TypeIndex(inst->VRegC()));
       break;
     case Instruction::kVerifyRegCWide:
       result = result && CheckWideRegisterIndex(inst->VRegC());
@@ -1260,9 +1270,9 @@
   return true;
 }
 
-inline bool MethodVerifier::CheckNewInstance(uint32_t idx) {
-  if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+inline bool MethodVerifier::CheckNewInstance(dex::TypeIndex idx) {
+  if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
                                       << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
@@ -1288,18 +1298,18 @@
   return true;
 }
 
-inline bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
-  if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+inline bool MethodVerifier::CheckTypeIndex(dex::TypeIndex idx) {
+  if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
                                       << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
   return true;
 }
 
-bool MethodVerifier::CheckNewArray(uint32_t idx) {
-  if (idx >= dex_file_->GetHeader().type_ids_size_) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
+bool MethodVerifier::CheckNewArray(dex::TypeIndex idx) {
+  if (idx.index_ >= dex_file_->GetHeader().type_ids_size_) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
                                       << dex_file_->GetHeader().type_ids_size_ << ")";
     return false;
   }
@@ -1935,7 +1945,7 @@
 
 // Returns the index of the first final instance field of the given class, or kDexNoIndex if there
 // is no such field.
-static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, uint16_t type_idx) {
+static uint32_t GetFirstFinalInstanceFieldIndex(const DexFile& dex_file, dex::TypeIndex type_idx) {
   const DexFile::ClassDef* class_def = dex_file.FindClassDef(type_idx);
   DCHECK(class_def != nullptr);
   const uint8_t* class_data = dex_file.GetClassData(*class_def);
@@ -2283,7 +2293,7 @@
     case Instruction::CONST_CLASS: {
       // Get type from instruction if unresolved then we need an access check
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+      const RegType& res_type = ResolveClassAndCheckAccess(dex::TypeIndex(inst->VRegB_21c()));
       // Register holds class, ie its type is class, on error it will hold Conflict.
       work_line_->SetRegisterType<LockOp::kClear>(
           this, inst->VRegA_21c(), res_type.IsConflict() ? res_type
@@ -2353,7 +2363,7 @@
        * dec_insn.vA when branching to a handler.
        */
       const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST);
-      const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c();
+      const dex::TypeIndex type_idx((is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c());
       const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
       if (res_type.IsConflict()) {
         // If this is a primitive type, fail HARD.
@@ -2423,7 +2433,7 @@
       break;
     }
     case Instruction::NEW_INSTANCE: {
-      const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+      const RegType& res_type = ResolveClassAndCheckAccess(dex::TypeIndex(inst->VRegB_21c()));
       if (res_type.IsConflict()) {
         DCHECK_NE(failures_.size(), 0U);
         break;  // bad class
@@ -2635,7 +2645,8 @@
         // ensure that subsequent merges don't lose type information - such as becoming an
         // interface from a class that would lose information relevant to field checks.
         const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c());
-        const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
+        const RegType& cast_type = ResolveClassAndCheckAccess(
+            dex::TypeIndex(instance_of_inst->VRegC_22c()));
 
         if (!orig_type.Equals(cast_type) &&
             !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
@@ -2873,7 +2884,8 @@
       if (return_type == nullptr) {
         uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-        uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+        dex::TypeIndex return_type_idx =
+            dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
         const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
         return_type = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
       }
@@ -2896,7 +2908,8 @@
         uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
         is_constructor = strcmp("<init>", dex_file_->StringDataByIdx(method_id.name_idx_)) == 0;
-        uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+        dex::TypeIndex return_type_idx =
+            dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
         return_type_descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
       } else {
         is_constructor = called_method->IsConstructor();
@@ -2972,7 +2985,8 @@
         if (called_method == nullptr) {
           uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
           const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-          uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+          dex::TypeIndex return_type_idx =
+              dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
           descriptor = dex_file_->StringByTypeIdx(return_type_idx);
         } else {
           descriptor = called_method->GetReturnTypeDescriptor();
@@ -3026,7 +3040,8 @@
       if (abs_method == nullptr) {
         uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-        uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+        dex::TypeIndex return_type_idx =
+            dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
         descriptor = dex_file_->StringByTypeIdx(return_type_idx);
       } else {
         descriptor = abs_method->GetReturnTypeDescriptor();
@@ -3490,8 +3505,8 @@
     ClassLinker* linker = Runtime::Current()->GetClassLinker();
 
     for (; iterator.HasNext(); iterator.Next()) {
-      uint16_t handler_type_idx = iterator.GetHandlerTypeIndex();
-      if (handler_type_idx == DexFile::kDexNoIndex16) {
+      dex::TypeIndex handler_type_idx = iterator.GetHandlerTypeIndex();
+      if (!handler_type_idx.IsValid()) {
         has_catch_all_handler = true;
       } else {
         // It is also a catch-all if it is java.lang.Throwable.
@@ -3618,7 +3633,7 @@
   return klass->IsInstantiable() || klass->IsPrimitive();
 }
 
-const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
+const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) {
   mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
   const RegType* result = nullptr;
   if (klass != nullptr) {
@@ -3674,7 +3689,7 @@
       CatchHandlerIterator iterator(handlers_ptr);
       for (; iterator.HasNext(); iterator.Next()) {
         if (iterator.GetHandlerAddress() == (uint32_t) work_insn_idx_) {
-          if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
+          if (!iterator.GetHandlerTypeIndex().IsValid()) {
             common_super = &reg_types_.JavaLangThrowable(false);
           } else {
             const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
@@ -3931,7 +3946,7 @@
                                       klass->CannotBeAssignedFromOtherTypes());
       } else {
         const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
-        const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
+        const dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
         res_method_class = &reg_types_.FromDescriptor(
             GetClassLoader(),
             dex_file_->StringByTypeIdx(class_idx),
@@ -4068,7 +4083,7 @@
   // If we're using invoke-super(method), make sure that the executing method's class' superclass
   // has a vtable entry for the target method. Or the target is on a interface.
   if (method_type == METHOD_SUPER) {
-    uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
+    dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
     const RegType& reference_type = reg_types_.FromDescriptor(
         GetClassLoader(),
         dex_file_->StringByTypeIdx(class_idx),
@@ -4277,16 +4292,16 @@
 }
 
 void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) {
-  uint32_t type_idx;
+  dex::TypeIndex type_idx;
   if (!is_filled) {
     DCHECK_EQ(inst->Opcode(), Instruction::NEW_ARRAY);
-    type_idx = inst->VRegC_22c();
+    type_idx = dex::TypeIndex(inst->VRegC_22c());
   } else if (!is_range) {
     DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY);
-    type_idx = inst->VRegB_35c();
+    type_idx = dex::TypeIndex(inst->VRegB_35c());
   } else {
     DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE);
-    type_idx = inst->VRegB_3rc();
+    type_idx = dex::TypeIndex(inst->VRegB_3rc());
   }
   const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
   if (res_type.IsConflict()) {  // bad class
@@ -5001,7 +5016,7 @@
     if (return_type_ == nullptr) {
       const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
       const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
-      uint16_t return_type_idx = proto_id.return_type_idx_;
+      dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
       const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
       return_type_ = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
     }
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index c6ce583..f3faecd 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -27,6 +27,7 @@
 #include "base/stl_util.h"
 #include "base/value_object.h"
 #include "dex_file.h"
+#include "dex_file_types.h"
 #include "handle.h"
 #include "instruction_flags.h"
 #include "method_reference.h"
@@ -261,7 +262,7 @@
     return have_any_pending_runtime_throw_failure_;
   }
 
-  const RegType& ResolveCheckedClass(uint32_t class_idx)
+  const RegType& ResolveCheckedClass(dex::TypeIndex class_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
   // Returns the method of a quick invoke or null if it cannot be found.
   ArtMethod* GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line,
@@ -471,18 +472,18 @@
 
   // Perform static checks on a "new-instance" instruction. Specifically, make sure the class
   // reference isn't for an array class.
-  bool CheckNewInstance(uint32_t idx);
+  bool CheckNewInstance(dex::TypeIndex idx);
 
   /* Ensure that the string index is in the valid range. */
   bool CheckStringIndex(uint32_t idx);
 
   // Perform static checks on an instruction that takes a class constant. Ensure that the class
   // index is in the valid range.
-  bool CheckTypeIndex(uint32_t idx);
+  bool CheckTypeIndex(dex::TypeIndex idx);
 
   // Perform static checks on a "new-array" instruction. Specifically, make sure they aren't
   // creating an array of arrays that causes the number of dimensions to exceed 255.
-  bool CheckNewArray(uint32_t idx);
+  bool CheckNewArray(dex::TypeIndex idx);
 
   // Verify an array data table. "cur_offset" is the offset of the fill-array-data instruction.
   bool CheckArrayData(uint32_t cur_offset);
@@ -625,7 +626,7 @@
 
   // Resolves a class based on an index and performs access checks to ensure the referrer can
   // access the resolved class.
-  const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
+  const RegType& ResolveClassAndCheckAccess(dex::TypeIndex class_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   /*
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index c395612..01af5ec 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -16,6 +16,9 @@
 
 #include "verifier_deps.h"
 
+#include <cstring>
+
+#include "base/stl_util.h"
 #include "compiler_callbacks.h"
 #include "leb128.h"
 #include "mirror/class-inl.h"
@@ -26,7 +29,6 @@
 namespace verifier {
 
 VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files) {
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
   for (const DexFile* dex_file : dex_files) {
     DCHECK(GetDexFileDeps(*dex_file) == nullptr);
     std::unique_ptr<DexFileDeps> deps(new DexFileDeps());
@@ -34,6 +36,28 @@
   }
 }
 
+void VerifierDeps::MergeWith(const VerifierDeps& other,
+                             const std::vector<const DexFile*>& dex_files) {
+  DCHECK(dex_deps_.size() == other.dex_deps_.size());
+  for (const DexFile* dex_file : dex_files) {
+    DexFileDeps* my_deps = GetDexFileDeps(*dex_file);
+    const DexFileDeps& other_deps = *other.GetDexFileDeps(*dex_file);
+    // We currently collect extra strings only on the main `VerifierDeps`,
+    // which should be the one passed as `this` in this method.
+    DCHECK(other_deps.strings_.empty());
+    MergeSets(my_deps->assignable_types_, other_deps.assignable_types_);
+    MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_);
+    MergeSets(my_deps->classes_, other_deps.classes_);
+    MergeSets(my_deps->fields_, other_deps.fields_);
+    MergeSets(my_deps->direct_methods_, other_deps.direct_methods_);
+    MergeSets(my_deps->virtual_methods_, other_deps.virtual_methods_);
+    MergeSets(my_deps->interface_methods_, other_deps.interface_methods_);
+    for (dex::TypeIndex entry : other_deps.unverified_classes_) {
+      my_deps->unverified_classes_.push_back(entry);
+    }
+  }
+}
+
 VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file) {
   auto it = dex_deps_.find(&dex_file);
   return (it == dex_deps_.end()) ? nullptr : it->second.get();
@@ -56,17 +80,114 @@
   }
 }
 
-template <typename T>
-uint32_t VerifierDeps::GetDeclaringClassStringId(const DexFile& dex_file, T* element) {
+uint32_t VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file,
+                                                  ObjPtr<mirror::Class> klass) {
+  DCHECK(klass != nullptr);
+  ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
+  // Array and proxy classes do not have a dex cache.
+  if (!klass->IsArrayClass() && !klass->IsProxyClass()) {
+    DCHECK(dex_cache != nullptr) << klass->PrettyClass();
+    if (dex_cache->GetDexFile() == &dex_file) {
+      // FindStringId is slow, try to go through the class def if we have one.
+      const DexFile::ClassDef* class_def = klass->GetClassDef();
+      DCHECK(class_def != nullptr) << klass->PrettyClass();
+      const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def->class_idx_);
+      if (kIsDebugBuild) {
+        std::string temp;
+        CHECK_EQ(GetIdFromString(dex_file, klass->GetDescriptor(&temp)), type_id.descriptor_idx_);
+      }
+      return type_id.descriptor_idx_;
+    }
+  }
+  std::string temp;
+  return GetIdFromString(dex_file, klass->GetDescriptor(&temp));
+}
+
+// Try to find the string descriptor of the class. type_idx is a best guess of a matching string id.
+static uint32_t TryGetClassDescriptorStringId(const DexFile& dex_file,
+                                              dex::TypeIndex type_idx,
+                                              ObjPtr<mirror::Class> klass)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!klass->IsArrayClass()) {
+    const DexFile::TypeId& type_id = dex_file.GetTypeId(type_idx);
+    const DexFile& klass_dex = klass->GetDexFile();
+    const DexFile::TypeId& klass_type_id = klass_dex.GetTypeId(klass->GetClassDef()->class_idx_);
+    if (strcmp(dex_file.GetTypeDescriptor(type_id),
+               klass_dex.GetTypeDescriptor(klass_type_id)) == 0) {
+      return type_id.descriptor_idx_;
+    }
+  }
+  return DexFile::kDexNoIndex;
+}
+
+uint32_t VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file,
+                                                       uint32_t dex_method_index,
+                                                       ArtMethod* method) {
   static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
-  if (element == nullptr) {
+  if (method == nullptr) {
     return VerifierDeps::kUnresolvedMarker;
-  } else {
-    std::string temp;
-    uint32_t string_id = GetIdFromString(
-        dex_file, element->GetDeclaringClass()->GetDescriptor(&temp));
+  }
+  const uint32_t string_id = TryGetClassDescriptorStringId(
+      dex_file,
+      dex_file.GetMethodId(dex_method_index).class_idx_,
+      method->GetDeclaringClass());
+  if (string_id != DexFile::kDexNoIndex) {
+    // Got lucky using the original dex file, return based on the input dex file.
+    DCHECK_EQ(GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()), string_id);
     return string_id;
   }
+  return GetClassDescriptorStringId(dex_file, method->GetDeclaringClass());
+}
+
+uint32_t VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file,
+                                                      uint32_t dex_field_idx,
+                                                      ArtField* field) {
+  static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
+  if (field == nullptr) {
+    return VerifierDeps::kUnresolvedMarker;
+  }
+  const uint32_t string_id = TryGetClassDescriptorStringId(
+      dex_file,
+      dex_file.GetFieldId(dex_field_idx).class_idx_,
+      field->GetDeclaringClass());
+  if (string_id != DexFile::kDexNoIndex) {
+    // Got lucky using the original dex file, return based on the input dex file.
+    DCHECK_EQ(GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()), string_id);
+    return string_id;
+  }
+  return GetClassDescriptorStringId(dex_file, field->GetDeclaringClass());
+}
+
+static inline VerifierDeps* GetMainVerifierDeps() {
+  // The main VerifierDeps is the one set in the compiler callbacks, which at the
+  // end of verification will have all the per-thread VerifierDeps merged into it.
+  CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks();
+  if (callbacks == nullptr) {
+    return nullptr;
+  }
+  return callbacks->GetVerifierDeps();
+}
+
+static inline VerifierDeps* GetThreadLocalVerifierDeps() {
+  // During AOT, each thread has its own VerifierDeps, to avoid lock contention. At the end
+  // of full verification, these VerifierDeps will be merged into the main one.
+  if (!Runtime::Current()->IsAotCompiler()) {
+    return nullptr;
+  }
+  return Thread::Current()->GetVerifierDeps();
+}
+
+static bool FindExistingStringId(const std::vector<std::string>& strings,
+                                 const std::string& str,
+                                 uint32_t* found_id) {
+  uint32_t num_extra_ids = strings.size();
+  for (size_t i = 0; i < num_extra_ids; ++i) {
+    if (strings[i] == str) {
+      *found_id = i;
+      return true;
+    }
+  }
+  return false;
 }
 
 uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::string& str) {
@@ -79,25 +200,32 @@
   // String is not in the DEX file. Assign a new ID to it which is higher than
   // the number of strings in the DEX file.
 
-  DexFileDeps* deps = GetDexFileDeps(dex_file);
+  // We use the main `VerifierDeps` for adding new strings to simplify
+  // synchronization/merging of these entries between threads.
+  VerifierDeps* singleton = GetMainVerifierDeps();
+  DexFileDeps* deps = singleton->GetDexFileDeps(dex_file);
   DCHECK(deps != nullptr);
 
   uint32_t num_ids_in_dex = dex_file.NumStringIds();
-  uint32_t num_extra_ids = deps->strings_.size();
+  uint32_t found_id;
 
-  for (size_t i = 0; i < num_extra_ids; ++i) {
-    if (deps->strings_[i] == str) {
-      return num_ids_in_dex + i;
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+    if (FindExistingStringId(deps->strings_, str, &found_id)) {
+      return num_ids_in_dex + found_id;
     }
   }
-
-  deps->strings_.push_back(str);
-
-  uint32_t new_id = num_ids_in_dex + num_extra_ids;
-  CHECK_GE(new_id, num_ids_in_dex);  // check for overflows
-  DCHECK_EQ(str, GetStringFromId(dex_file, new_id));
-
-  return new_id;
+  {
+    WriterMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+    if (FindExistingStringId(deps->strings_, str, &found_id)) {
+      return num_ids_in_dex + found_id;
+    }
+    deps->strings_.push_back(str);
+    uint32_t new_id = num_ids_in_dex + deps->strings_.size() - 1;
+    CHECK_GE(new_id, num_ids_in_dex);  // check for overflows
+    DCHECK_EQ(str, singleton->GetStringFromId(dex_file, new_id));
+    return new_id;
+  }
 }
 
 std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const {
@@ -137,7 +265,7 @@
 }
 
 void VerifierDeps::AddClassResolution(const DexFile& dex_file,
-                                      uint16_t type_idx,
+                                      dex::TypeIndex type_idx,
                                       mirror::Class* klass) {
   DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
   if (dex_deps == nullptr) {
@@ -151,7 +279,6 @@
     return;
   }
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
   dex_deps->classes_.emplace(ClassResolution(type_idx, GetAccessFlags(klass)));
 }
 
@@ -170,9 +297,11 @@
     return;
   }
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-  dex_deps->fields_.emplace(FieldResolution(
-      field_idx, GetAccessFlags(field), GetDeclaringClassStringId(dex_file, field)));
+  dex_deps->fields_.emplace(FieldResolution(field_idx,
+                                            GetAccessFlags(field),
+                                            GetFieldDeclaringClassStringId(dex_file,
+                                                                           field_idx,
+                                                                           field)));
 }
 
 void VerifierDeps::AddMethodResolution(const DexFile& dex_file,
@@ -191,10 +320,9 @@
     return;
   }
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
   MethodResolution method_tuple(method_idx,
                                 GetAccessFlags(method),
-                                GetDeclaringClassStringId(dex_file, method));
+                                GetMethodDeclaringClassStringId(dex_file, method_idx, method));
   if (resolution_kind == kDirectMethodResolution) {
     dex_deps->direct_methods_.emplace(method_tuple);
   } else if (resolution_kind == kVirtualMethodResolution) {
@@ -260,15 +388,9 @@
     return;
   }
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
   // Get string IDs for both descriptors and store in the appropriate set.
-
-  std::string temp1, temp2;
-  std::string destination_desc(destination->GetDescriptor(&temp1));
-  std::string source_desc(source->GetDescriptor(&temp2));
-  uint32_t destination_id = GetIdFromString(dex_file, destination_desc);
-  uint32_t source_id = GetIdFromString(dex_file, source_desc);
+  uint32_t destination_id = GetClassDescriptorStringId(dex_file, destination);
+  uint32_t source_id = GetClassDescriptorStringId(dex_file, source);
 
   if (is_assignable) {
     dex_deps->assignable_types_.emplace(TypeAssignability(destination_id, source_id));
@@ -277,45 +399,36 @@
   }
 }
 
-static inline VerifierDeps* GetVerifierDepsSingleton() {
-  CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks();
-  if (callbacks == nullptr) {
-    return nullptr;
-  }
-  return callbacks->GetVerifierDeps();
-}
-
 void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file,
-                                                 uint16_t type_idx,
+                                                 dex::TypeIndex type_idx,
                                                  MethodVerifier::FailureKind failure_kind) {
   if (failure_kind == MethodVerifier::kNoFailure) {
     // We only record classes that did not fully verify at compile time.
     return;
   }
 
-  VerifierDeps* singleton = GetVerifierDepsSingleton();
-  if (singleton != nullptr) {
-    DexFileDeps* dex_deps = singleton->GetDexFileDeps(dex_file);
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
+  VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+  if (thread_deps != nullptr) {
+    DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file);
     dex_deps->unverified_classes_.push_back(type_idx);
   }
 }
 
 void VerifierDeps::MaybeRecordClassResolution(const DexFile& dex_file,
-                                              uint16_t type_idx,
+                                              dex::TypeIndex type_idx,
                                               mirror::Class* klass) {
-  VerifierDeps* singleton = GetVerifierDepsSingleton();
-  if (singleton != nullptr) {
-    singleton->AddClassResolution(dex_file, type_idx, klass);
+  VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+  if (thread_deps != nullptr) {
+    thread_deps->AddClassResolution(dex_file, type_idx, klass);
   }
 }
 
 void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file,
                                               uint32_t field_idx,
                                               ArtField* field) {
-  VerifierDeps* singleton = GetVerifierDepsSingleton();
-  if (singleton != nullptr) {
-    singleton->AddFieldResolution(dex_file, field_idx, field);
+  VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+  if (thread_deps != nullptr) {
+    thread_deps->AddFieldResolution(dex_file, field_idx, field);
   }
 }
 
@@ -323,9 +436,9 @@
                                                uint32_t method_idx,
                                                MethodResolutionKind resolution_kind,
                                                ArtMethod* method) {
-  VerifierDeps* singleton = GetVerifierDepsSingleton();
-  if (singleton != nullptr) {
-    singleton->AddMethodResolution(dex_file, method_idx, resolution_kind, method);
+  VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+  if (thread_deps != nullptr) {
+    thread_deps->AddMethodResolution(dex_file, method_idx, resolution_kind, method);
   }
 }
 
@@ -334,42 +447,68 @@
                                             mirror::Class* source,
                                             bool is_strict,
                                             bool is_assignable) {
-  VerifierDeps* singleton = GetVerifierDepsSingleton();
-  if (singleton != nullptr) {
-    singleton->AddAssignability(dex_file, destination, source, is_strict, is_assignable);
+  VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
+  if (thread_deps != nullptr) {
+    thread_deps->AddAssignability(dex_file, destination, source, is_strict, is_assignable);
   }
 }
 
+namespace {
+
 static inline uint32_t DecodeUint32WithOverflowCheck(const uint8_t** in, const uint8_t* end) {
   CHECK_LT(*in, end);
   return DecodeUnsignedLeb128(in);
 }
 
+template<typename T> inline uint32_t Encode(T in);
+
+template<> inline uint32_t Encode<uint16_t>(uint16_t in) {
+  return in;
+}
+template<> inline uint32_t Encode<uint32_t>(uint32_t in) {
+  return in;
+}
+template<> inline uint32_t Encode<dex::TypeIndex>(dex::TypeIndex in) {
+  return in.index_;
+}
+
+template<typename T> inline T Decode(uint32_t in);
+
+template<> inline uint16_t Decode<uint16_t>(uint32_t in) {
+  return dchecked_integral_cast<uint16_t>(in);
+}
+template<> inline uint32_t Decode<uint32_t>(uint32_t in) {
+  return in;
+}
+template<> inline dex::TypeIndex Decode<dex::TypeIndex>(uint32_t in) {
+  return dex::TypeIndex(in);
+}
+
 template<typename T1, typename T2>
 static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) {
-  EncodeUnsignedLeb128(out, std::get<0>(t));
-  EncodeUnsignedLeb128(out, std::get<1>(t));
+  EncodeUnsignedLeb128(out, Encode(std::get<0>(t)));
+  EncodeUnsignedLeb128(out, Encode(std::get<1>(t)));
 }
 
 template<typename T1, typename T2>
 static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2>* t) {
-  T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end));
-  T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
+  T1 v1 = Decode<T1>(DecodeUint32WithOverflowCheck(in, end));
+  T2 v2 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end));
   *t = std::make_tuple(v1, v2);
 }
 
 template<typename T1, typename T2, typename T3>
 static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2, T3>& t) {
-  EncodeUnsignedLeb128(out, std::get<0>(t));
-  EncodeUnsignedLeb128(out, std::get<1>(t));
-  EncodeUnsignedLeb128(out, std::get<2>(t));
+  EncodeUnsignedLeb128(out, Encode(std::get<0>(t)));
+  EncodeUnsignedLeb128(out, Encode(std::get<1>(t)));
+  EncodeUnsignedLeb128(out, Encode(std::get<2>(t)));
 }
 
 template<typename T1, typename T2, typename T3>
 static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2, T3>* t) {
-  T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end));
-  T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
-  T3 v3 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end));
+  T1 v1 = Decode<T1>(DecodeUint32WithOverflowCheck(in, end));
+  T2 v2 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end));
+  T3 v3 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end));
   *t = std::make_tuple(v1, v2, v3);
 }
 
@@ -381,11 +520,12 @@
   }
 }
 
+template <typename T>
 static inline void EncodeUint16Vector(std::vector<uint8_t>* out,
-                                      const std::vector<uint16_t>& vector) {
+                                      const std::vector<T>& vector) {
   EncodeUnsignedLeb128(out, vector.size());
-  for (uint16_t entry : vector) {
-    EncodeUnsignedLeb128(out, entry);
+  for (const T& entry : vector) {
+    EncodeUnsignedLeb128(out, Encode(entry));
   }
 }
 
@@ -400,14 +540,16 @@
   }
 }
 
+template<typename T>
 static inline void DecodeUint16Vector(const uint8_t** in,
                                       const uint8_t* end,
-                                      std::vector<uint16_t>* vector) {
+                                      std::vector<T>* vector) {
   DCHECK(vector->empty());
   size_t num_entries = DecodeUint32WithOverflowCheck(in, end);
   vector->reserve(num_entries);
   for (size_t i = 0; i < num_entries; ++i) {
-    vector->push_back(dchecked_integral_cast<uint16_t>(DecodeUint32WithOverflowCheck(in, end)));
+    vector->push_back(
+        Decode<T>(dchecked_integral_cast<uint16_t>(DecodeUint32WithOverflowCheck(in, end))));
   }
 }
 
@@ -436,9 +578,10 @@
   }
 }
 
+}  // namespace
+
 void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
                           std::vector<uint8_t>* buffer) const {
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
   for (const DexFile* dex_file : dex_files) {
     const DexFileDeps& deps = *GetDexFileDeps(*dex_file);
     EncodeStringVector(buffer, deps.strings_);
@@ -480,8 +623,6 @@
 }
 
 bool VerifierDeps::Equals(const VerifierDeps& rhs) const {
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
   if (dex_deps_.size() != rhs.dex_deps_.size()) {
     return false;
   }
@@ -599,7 +740,7 @@
       }
     }
 
-    for (uint16_t type_index : dep.second->unverified_classes_) {
+    for (dex::TypeIndex type_index : dep.second->unverified_classes_) {
       vios->Stream()
           << dex_file.StringByTypeIdx(type_index)
           << " is expected to be verified at runtime\n";
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 7b419d4..a12071b 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -42,29 +42,30 @@
 // which are being compiled. Classes defined in DEX files outside of this set
 // (or synthesized classes without associated DEX files) are considered being
 // in the classpath.
-// During code-flow verification, the MethodVerifier informs the VerifierDeps
-// singleton about the outcome of every resolution and assignability test, and
-// the singleton records them if their outcome may change with changes in the
-// classpath.
+// During code-flow verification, the MethodVerifier informs VerifierDeps
+// about the outcome of every resolution and assignability test, and
+// the VerifierDeps object records them if their outcome may change with
+// changes in the classpath.
 class VerifierDeps {
  public:
-  explicit VerifierDeps(const std::vector<const DexFile*>& dex_files)
-      REQUIRES(!Locks::verifier_deps_lock_);
+  explicit VerifierDeps(const std::vector<const DexFile*>& dex_files);
 
-  VerifierDeps(const std::vector<const DexFile*>& dex_files,
-               ArrayRef<const uint8_t> data)
-      REQUIRES(!Locks::verifier_deps_lock_);
+  VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<const uint8_t> data);
+
+  // Merge `other` into this `VerifierDeps`'. `other` and `this` must be for the
+  // same set of dex files.
+  void MergeWith(const VerifierDeps& other, const std::vector<const DexFile*>& dex_files);
 
   // Record the verification status of the class at `type_idx`.
   static void MaybeRecordVerificationStatus(const DexFile& dex_file,
-                                            uint16_t type_idx,
+                                            dex::TypeIndex type_idx,
                                             MethodVerifier::FailureKind failure_kind)
       REQUIRES(!Locks::verifier_deps_lock_);
 
   // Record the outcome `klass` of resolving type `type_idx` from `dex_file`.
   // If `klass` is null, the class is assumed unresolved.
   static void MaybeRecordClassResolution(const DexFile& dex_file,
-                                         uint16_t type_idx,
+                                         dex::TypeIndex type_idx,
                                          mirror::Class* klass)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::verifier_deps_lock_);
@@ -101,38 +102,30 @@
   // Serialize the recorded dependencies and store the data into `buffer`.
   // `dex_files` provides the order of the dex files in which the dependencies
   // should be emitted.
-  void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const
-      REQUIRES(!Locks::verifier_deps_lock_);
+  void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const;
 
-  // NO_THREAD_SAFETY_ANALYSIS as Dump iterates over dex_deps_, which is guarded by
-  // verifier_deps_lock_, but we expect Dump to be called once the deps collection is done.
-  void Dump(VariableIndentationOutputStream* vios) const
-      NO_THREAD_SAFETY_ANALYSIS;
+  void Dump(VariableIndentationOutputStream* vios) const;
 
   // Verify the encoded dependencies of this `VerifierDeps` are still valid.
-  // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`.
   bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
-      NO_THREAD_SAFETY_ANALYSIS;
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // NO_THREAD_SAFETY_ANALSYS, as this is queried when the VerifierDeps are
-  // fully created.
-  const std::vector<uint16_t>& GetUnverifiedClasses(const DexFile& dex_file) const
-      NO_THREAD_SAFETY_ANALYSIS {
+  const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
     return GetDexFileDeps(dex_file)->unverified_classes_;
   }
 
  private:
   static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
 
-  using ClassResolutionBase = std::tuple<uint32_t, uint16_t>;
+  using ClassResolutionBase = std::tuple<dex::TypeIndex, uint16_t>;
   struct ClassResolution : public ClassResolutionBase {
     ClassResolution() = default;
     ClassResolution(const ClassResolution&) = default;
-    ClassResolution(uint32_t type_idx, uint16_t access_flags)
+    ClassResolution(dex::TypeIndex type_idx, uint16_t access_flags)
         : ClassResolutionBase(type_idx, access_flags) {}
 
     bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; }
-    uint32_t GetDexTypeIndex() const { return std::get<0>(*this); }
+    dex::TypeIndex GetDexTypeIndex() const { return std::get<0>(*this); }
     uint16_t GetAccessFlags() const { return std::get<1>(*this); }
   };
 
@@ -193,20 +186,16 @@
     std::set<MethodResolution> interface_methods_;
 
     // List of classes that were not fully verified in that dex file.
-    std::vector<uint16_t> unverified_classes_;
+    std::vector<dex::TypeIndex> unverified_classes_;
 
     bool Equals(const DexFileDeps& rhs) const;
   };
 
   // Finds the DexFileDep instance associated with `dex_file`, or nullptr if
   // `dex_file` is not reported as being compiled.
-  // We disable thread safety analysis. The method only reads the key set of
-  // `dex_deps_` which stays constant after initialization.
-  DexFileDeps* GetDexFileDeps(const DexFile& dex_file)
-      NO_THREAD_SAFETY_ANALYSIS;
+  DexFileDeps* GetDexFileDeps(const DexFile& dex_file);
 
-  const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const
-      NO_THREAD_SAFETY_ANALYSIS;
+  const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const;
 
   // Returns true if `klass` is null or not defined in any of dex files which
   // were reported as being compiled.
@@ -218,11 +207,10 @@
   // of the corresponding DexFileDeps structure (either provided or inferred from
   // `dex_file`).
   uint32_t GetIdFromString(const DexFile& dex_file, const std::string& str)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES(!Locks::verifier_deps_lock_);
 
   // Returns the string represented by `id`.
-  std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const
-      REQUIRES(Locks::verifier_deps_lock_);
+  std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const;
 
   // Returns the bytecode access flags of `element` (bottom 16 bits), or
   // `kUnresolvedMarker` if `element` is null.
@@ -232,13 +220,22 @@
 
   // Returns a string ID of the descriptor of the declaring class of `element`,
   // or `kUnresolvedMarker` if `element` is null.
-  template <typename T>
-  uint32_t GetDeclaringClassStringId(const DexFile& dex_file, T* element)
+  uint32_t GetMethodDeclaringClassStringId(const DexFile& dex_file,
+                                           uint32_t dex_method_idx,
+                                           ArtMethod* method)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  uint32_t GetFieldDeclaringClassStringId(const DexFile& dex_file,
+                                          uint32_t dex_field_idx,
+                                          ArtField* field)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Returns a string ID of the descriptor of the class.
+  uint32_t GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES(!Locks::verifier_deps_lock_);
 
   void AddClassResolution(const DexFile& dex_file,
-                          uint16_t type_idx,
+                          dex::TypeIndex type_idx,
                           mirror::Class* klass)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::verifier_deps_lock_);
@@ -261,11 +258,9 @@
                         mirror::Class* source,
                         bool is_strict,
                         bool is_assignable)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Locks::verifier_deps_lock_);
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool Equals(const VerifierDeps& rhs) const
-      REQUIRES(!Locks::verifier_deps_lock_);
+  bool Equals(const VerifierDeps& rhs) const;
 
   // Verify `dex_file` according to the `deps`, that is going over each
   // `DexFileDeps` field, and checking that the recorded information still
@@ -274,16 +269,14 @@
                      const DexFile& dex_file,
                      const DexFileDeps& deps,
                      Thread* self) const
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
                            const DexFile& dex_file,
                            const std::set<TypeAssignability>& assignables,
                            bool expected_assignability,
                            Thread* self) const
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Verify that the set of resolved classes at the point of creation
   // of this `VerifierDeps` is still the same.
@@ -291,8 +284,7 @@
                      const DexFile& dex_file,
                      const std::set<ClassResolution>& classes,
                      Thread* self) const
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Verify that the set of resolved fields at the point of creation
   // of this `VerifierDeps` is still the same, and each field resolves to the
@@ -302,7 +294,7 @@
                     const std::set<FieldResolution>& classes,
                     Thread* self) const
       REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES(!Locks::verifier_deps_lock_);
 
   // Verify that the set of resolved methods at the point of creation
   // of this `VerifierDeps` is still the same, and each method resolves to the
@@ -312,12 +304,10 @@
                      const std::set<MethodResolution>& methods,
                      MethodResolutionKind kind,
                      Thread* self) const
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(Locks::verifier_deps_lock_);
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Map from DexFiles into dependencies collected from verification of their methods.
-  std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_
-      GUARDED_BY(Locks::verifier_deps_lock_);
+  std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_;
 
   friend class VerifierDepsTest;
   ART_FRIEND_TEST(VerifierDepsTest, StringToId);
diff --git a/test/015-switch/src/Main.java b/test/015-switch/src/Main.java
index 2a7995a..2b724a1 100644
--- a/test/015-switch/src/Main.java
+++ b/test/015-switch/src/Main.java
@@ -113,7 +113,7 @@
     }
 
     // Long packed-switch that might lead to not creating chained-ifs.
-    public static void packedSwitch7(int value) {
+    public static long packedSwitch7(int value) {
         switch (value) {
             case 1:
                 System.out.println(1); break;
@@ -148,6 +148,113 @@
             default:
                 System.out.println("default"); break;
         }
+
+        // Jump tables previously were emitted in the end of the method code buffer. The
+        // following boilerplate code aims to fill the emitted code buffer extensively
+        // and check that even for big method jump table is correctly emitted, its address
+        // is within a range of corresponded pc-relative instructions (this applies to
+        // ARM mainly).
+        long temp = value;
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+        temp = Long.rotateLeft(temp, value);
+
+        return temp;
     }
 
     // Sparse switch, just leave a gap.
diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt
index e69de29..b0aad4d 100644
--- a/test/445-checker-licm/expected.txt
+++ b/test/445-checker-licm/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
index 061fe6e..00ce3a9 100644
--- a/test/445-checker-licm/src/Main.java
+++ b/test/445-checker-licm/src/Main.java
@@ -164,8 +164,43 @@
     return result;
   }
 
+  //
+  // All operations up to the null check can be hoisted out of the
+  // loop. The null check itself sees the induction in its environment.
+  //
+  /// CHECK-START: int Main.doWhile(int) licm (before)
+  /// CHECK-DAG: <<Add:i\d+>> Add                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:              LoadClass           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:l\d+>> StaticFieldGet      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:              NullCheck [<<Get>>] env:[[<<Add>>,<<Get>>,{{i\d+}}]] loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:              ArrayLength         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:              BoundsCheck         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:              ArrayGet            loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.doWhile(int) licm (after)
+  /// CHECK-NOT: LoadClass      loop:{{B\d+}}
+  /// CHECK-NOT: StaticFieldGet loop:{{B\d+}}
+  //
+  /// CHECK-START: int Main.doWhile(int) licm (after)
+  /// CHECK-DAG:              LoadClass           loop:none
+  /// CHECK-DAG: <<Get:l\d+>> StaticFieldGet      loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add                 loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:              NullCheck [<<Get>>] env:[[<<Add>>,<<Get>>,{{i\d+}}]] loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:              ArrayLength         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:              BoundsCheck         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:              ArrayGet            loop:<<Loop>>      outer_loop:none
+  public static int doWhile(int k) {
+    int i = k;
+    do {
+      i += 2;
+    } while (staticArray[i] == 0);
+    return i;
+  }
+
   public static int staticField = 42;
 
+  public static int[] staticArray = null;
+
   public static void assertEquals(int expected, int actual) {
     if (expected != actual) {
       throw new Error("Expected " + expected + ", got " + actual);
@@ -181,5 +216,24 @@
     assertEquals(21, divAndIntrinsic(new int[] { 4, -2, 8, -3 }));
     assertEquals(45, invariantBoundIntrinsic(-10));
     assertEquals(30, invariantBodyIntrinsic(2, 3));
+
+    staticArray = null;
+    try {
+      doWhile(0);
+      throw new Error("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    staticArray = new int[5];
+    staticArray[4] = 1;
+    assertEquals(4, doWhile(-2));
+    assertEquals(4, doWhile(0));
+    assertEquals(4, doWhile(2));
+    try {
+      doWhile(1);
+      throw new Error("Expected IOOBE");
+    } catch (IndexOutOfBoundsException e) {
+    }
+
+    System.out.println("passed");
   }
 }
diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java
index 6b5c657..209786a 100644
--- a/test/530-checker-loops3/src/Main.java
+++ b/test/530-checker-loops3/src/Main.java
@@ -246,7 +246,7 @@
 
     oneConstantIndex(a, b);
     for (int i = 0; i < a.length; i++) {
-      expectEquals(2, a[i]);;
+      expectEquals(2, a[i]);
     }
     try {
       oneConstantIndex(a, b1);
@@ -256,7 +256,7 @@
 
     multipleConstantIndices(a, b);
     for (int i = 0; i < a.length; i++) {
-      expectEquals(6, a[i]);;
+      expectEquals(6, a[i]);
     }
     try {
       multipleConstantIndices(a, b1);
@@ -266,7 +266,7 @@
 
     oneInvariantIndex(a, b, 1);
     for (int i = 0; i < a.length; i++) {
-      expectEquals(2, a[i]);;
+      expectEquals(2, a[i]);
     }
     try {
       oneInvariantIndex(a, b1, 1);
@@ -276,7 +276,7 @@
 
     multipleInvariantIndices(a, b, 1);
     for (int i = 0; i < a.length; i++) {
-      expectEquals(6, a[i]);;
+      expectEquals(6, a[i]);
     }
     try {
       multipleInvariantIndices(a, b1, 1);
@@ -286,18 +286,18 @@
 
     oneUnitStride(a, b);
     for (int i = 0; i < a.length; i++) {
-      expectEquals(i + 1, a[i]);;
+      expectEquals(i + 1, a[i]);
     }
     try {
       oneUnitStride(a, b1);
       throw new Error("Should throw AIOOBE");
     } catch (ArrayIndexOutOfBoundsException e) {
-      expectEquals(100, a[0]);;
+      expectEquals(100, a[0]);
     }
 
     multipleUnitStrides(a, b);
     for (int i = 1; i < a.length - 1; i++) {
-      expectEquals(3 * i + 3, a[i]);;
+      expectEquals(3 * i + 3, a[i]);
     }
     try {
       multipleUnitStrides(a, b1);
@@ -308,7 +308,7 @@
     multipleUnitStridesConditional(a, b);
     for (int i = 2; i < a.length - 2; i++) {
       int e = 3 * i + 3 + (((i & 1) == 0) ? i + 2 : i);
-      expectEquals(e, a[i]);;
+      expectEquals(e, a[i]);
     }
     try {
       multipleUnitStridesConditional(a, b1);
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index 02c609e..6b25747 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -30,7 +30,7 @@
 
   /// CHECK-START-ARM: int Main.and255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                and {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                and {{r\d+}}, {{r\d+}}, #0xff
 
   public static int and255(int arg) {
     return arg & 255;
@@ -46,7 +46,7 @@
 
   /// CHECK-START-ARM: int Main.andNot15(int) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #0xf
 
   public static int andNot15(int arg) {
     return arg & ~15;
@@ -54,7 +54,7 @@
 
   /// CHECK-START-ARM: int Main.or255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #0xff
 
   public static int or255(int arg) {
     return arg | 255;
@@ -70,7 +70,7 @@
 
   /// CHECK-START-ARM: int Main.orNot15(int) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK:                orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                orn {{r\d+}}, {{r\d+}}, #0xf
 
   public static int orNot15(int arg) {
     return arg | ~15;
@@ -78,7 +78,7 @@
 
   /// CHECK-START-ARM: int Main.xor255(int) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #0xff
 
   public static int xor255(int arg) {
     return arg ^ 255;
@@ -104,7 +104,7 @@
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #255
+  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #0xff
   /// CHECK-DAG:            movs {{r\d+}}, #0
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
@@ -131,7 +131,7 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK:                bic {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
 
@@ -144,8 +144,8 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
-  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            bic {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            bic {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            and{{(\.w)?}}
   /// CHECK-NOT:            bic{{(\.w)?}}
 
@@ -157,7 +157,7 @@
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                orr {{r\d+}}, {{r\d+}}, #0xff
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
@@ -183,7 +183,7 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-DAG:            mvn {{r\d+}}, #0
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
@@ -197,8 +197,8 @@
   /// CHECK-NOT:            mvn {{r\d+}}, #15
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
-  /// CHECK-DAG:            orr {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
+  /// CHECK-DAG:            orr {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #0xf
   /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
@@ -209,7 +209,7 @@
   /// CHECK-START-ARM: long Main.xor255(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
   /// CHECK-NOT:            eor{{(\.w)?}}
-  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #255
+  /// CHECK:                eor {{r\d+}}, {{r\d+}}, #0xff
   /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor255(long arg) {
@@ -257,8 +257,8 @@
   /// CHECK-NOT:            movs {{r\d+}}, #15
   /// CHECK-NOT:            mov.w {{r\d+}}, #-268435456
   /// CHECK-NOT:            eor{{(\.w)?}}
-  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #4026531840
+  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #0xf
+  /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #0xf0000000
   /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor0xf00000000000000f(long arg) {
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index e0782bc..0ea7d34 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -100,7 +100,7 @@
   /// CHECK-DAG:                     Return [<<ArrayGet2>>]
   public static float test1() {
     Test1 test1 = getNullTest1();
-    Test2 test2 = getNullTest2();;
+    Test2 test2 = getNullTest2();
     int[] iarr = test1.iarr;
     float[] farr = test2.farr;
     iarr[0] = iarr[1];
diff --git a/test/611-checker-simplify-if/src/Main.java b/test/611-checker-simplify-if/src/Main.java
index 774f239..c1d75ec 100644
--- a/test/611-checker-simplify-if/src/Main.java
+++ b/test/611-checker-simplify-if/src/Main.java
@@ -144,7 +144,7 @@
   /// CHECK-NOT:                          GreaterThanOrEqual
   /// CHECK-NOT:                          If
   public static void testGreaterCondition(String[] args) {
-    int a = 42;;
+    int a = 42;
     if (args.length == 42) {
       a = 34;
     } else {
diff --git a/test/625-checker-licm-regressions/src/Main.java b/test/625-checker-licm-regressions/src/Main.java
index cc1e07c..f372b1c 100644
--- a/test/625-checker-licm-regressions/src/Main.java
+++ b/test/625-checker-licm-regressions/src/Main.java
@@ -47,14 +47,83 @@
     } while (j < arr.length);
   }
 
+  //
+  // Similar situation as in foo(), but now a proper induction value
+  // is assigned to the field inside the do-while loop.
+  //
+  /// CHECK-START: void Main.bar(int[]) licm (before)
+  /// CHECK-DAG: LoadClass      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: StaticFieldSet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: NullCheck      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArrayLength    loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.bar(int[]) licm (after)
+  /// CHECK-DAG: LoadClass      loop:none
+  /// CHECK-DAG: StaticFieldSet loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: NullCheck      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArrayLength    loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.bar(int[]) licm (after)
+  /// CHECK-NOT: LoadClass      loop:{{B\d+}} outer_loop:none
+  static void bar(int[] arr) {
+    int j = 0;
+    do {
+      j++;
+      sA = j;
+    } while (j < arr.length);
+  }
+
+  //
+  // Similar situation as in bar(), but now an explicit catch
+  // statement may need the latest value of local j.
+  //
+  /// CHECK-START: int Main.catcher(int[]) licm (before)
+  /// CHECK-DAG: NullCheck   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayLength loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.catcher(int[]) licm (after)
+  /// CHECK-DAG: NullCheck   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayLength loop:<<Loop>>      outer_loop:none
+  static int catcher(int[] arr) {
+    int j = 0;
+    try {
+      do {
+        j++;
+      } while (j < arr.length);
+    } catch (NullPointerException e) {
+      return -j;  // flag exception with negative value
+    }
+    return j;
+  }
+
   public static void main(String[] args) {
     sA = 0;
     try {
       foo(null);
-    } catch (Exception e) {
+      throw new Error("Expected NPE");
+    } catch (NullPointerException e) {
     }
     expectEquals(1, sA);
 
+    sA = 0;
+    try {
+      bar(null);
+      throw new Error("Expected NPE");
+    } catch (NullPointerException e) {
+    }
+    expectEquals(1, sA);
+
+    for (int i = 0; i < 5; i++) {
+      sA = 0;
+      bar(new int[i]);
+      expectEquals(i == 0 ? 1 : i, sA);
+    }
+
+    expectEquals(-1, catcher(null));
+    for (int i = 0; i < 5; i++) {
+      expectEquals(i == 0 ? 1 : i, catcher(new int[i]));
+    }
+
     System.out.println("passed");
   }
 
diff --git a/test/626-set-resolved-string/expected.txt b/test/626-set-resolved-string/expected.txt
new file mode 100644
index 0000000..f4983b5
--- /dev/null
+++ b/test/626-set-resolved-string/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+foo
diff --git a/test/626-set-resolved-string/info.txt b/test/626-set-resolved-string/info.txt
new file mode 100644
index 0000000..e3a512f
--- /dev/null
+++ b/test/626-set-resolved-string/info.txt
@@ -0,0 +1,3 @@
+Test that even if Java code calls DexCache.setResolvedString and does
+not strongly intern the given string, the JIT will ensure that the
+strings it references are strongly interned.
diff --git a/test/626-set-resolved-string/src/Main.java b/test/626-set-resolved-string/src/Main.java
new file mode 100644
index 0000000..868b9d1
--- /dev/null
+++ b/test/626-set-resolved-string/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.lang.reflect.Method;
+
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+
+    // Get all methods. We cannot call getDeclaredMethod("foo") as
+    // that would make "foo" a strong root.
+    Method[] methods = Main.class.getDeclaredMethods();
+
+    // Call getName on the methods, which is implemented by using the dex
+    // cache and  calling setResolvedString.
+    for (int i = 0; i < methods.length; i++) {
+      methods[i].getName();
+    }
+
+    // Compile Main.foo. "foo" needs to be a strong root for JIT compilation.
+    // We stress test this:
+    //   - avoid strongly interning "foo" by doing "f" + "oo"
+    //   - call GC so that weaks can be collected.
+    //   - invoke foo() to make sure "foo" hasn't been collected.
+    ensureJitCompiled(Main.class, "f" + "oo");
+    Runtime.getRuntime().gc();
+    foo();
+  }
+
+  public static void foo() {
+    System.out.println("foo");
+  }
+
+  public static native void ensureJitCompiled(Class cls, String method_name);
+}
diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt
new file mode 100644
index 0000000..d0f61f6
--- /dev/null
+++ b/test/628-vdex/expected.txt
@@ -0,0 +1,2 @@
+In foo
+In foo
diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/628-vdex/info.txt
diff --git a/test/103-string-append/run b/test/628-vdex/run
old mode 100755
new mode 100644
similarity index 77%
rename from test/103-string-append/run
rename to test/628-vdex/run
index e27a622..f1b0a95
--- a/test/103-string-append/run
+++ b/test/628-vdex/run
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# 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.
@@ -14,5 +14,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# As this is a performance test we always use the non-debug build.
-exec ${RUN} "${@/#libartd.so/libart.so}"
+exec ${RUN} --vdex "${@}"
diff --git a/test/628-vdex/src/Main.java b/test/628-vdex/src/Main.java
new file mode 100644
index 0000000..7ceab2c
--- /dev/null
+++ b/test/628-vdex/src/Main.java
@@ -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.
+ */
+
+public class Main {
+  Main() {
+    // Will be quickened with RETURN_VOID_NO_BARRIER.
+  }
+
+  public static void main(String[] args) {
+    Main m = new Main();
+    Object o = m;
+    // The call and field accesses will be quickened.
+    m.foo(m.a);
+
+    // The checkcast will be quickened.
+    m.foo(((Main)o).a);
+  }
+
+  int a;
+  void foo(int a) {
+    System.out.println("In foo");
+  }
+}
+
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index 8f0850b..3ed91d7 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -66,6 +66,15 @@
     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
   }
 
+  // Also run GetMethodName with all parameter pointers null to check for segfaults.
+  jvmtiError result2 = jvmti_env->GetMethodName(id, nullptr, nullptr, nullptr);
+  if (result2 != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(result2, &err);
+    printf("Failure running GetMethodName(null, null, null): %s\n", err);
+    return nullptr;
+  }
+
   return ret;
 }
 
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index d1ddbae..8002cfa 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -45,7 +45,7 @@
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
-root@root --(jni-local)--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local)--> 1@1000 [size=16, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
@@ -67,7 +67,7 @@
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
-root@root --(jni-local)--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local)--> 1@1000 [size=16, length=-1]
 root@root --(stack-local)--> 2@1000 [size=16, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 0c627d6..340671d 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -185,20 +185,229 @@
         }
       }
 
-      lines_.push_back(
-          StringPrintf("%s --(%s)--> %" PRId64 "@%" PRId64 " [size=%" PRId64 ", length=%d]",
-                       referrer_str.c_str(),
-                       GetReferenceTypeStr(reference_kind, reference_info).c_str(),
-                       *tag_ptr,
-                       class_tag,
-                       adapted_size,
-                       length));
+      std::string referree_str = StringPrintf("%" PRId64 "@%" PRId64, *tag_ptr, class_tag);
+
+      lines_.push_back(CreateElem(referrer_str,
+                                  referree_str,
+                                  reference_kind,
+                                  reference_info,
+                                  adapted_size,
+                                  length));
 
       if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && *tag_ptr == 1000) {
         DumpStacks();
       }
     }
 
+    std::vector<std::string> GetLines() const {
+      std::vector<std::string> ret;
+      for (const std::unique_ptr<Elem>& e : lines_) {
+        ret.push_back(e->Print());
+      }
+      return ret;
+    }
+
+   private:
+    // We need to postpone some printing, as required functions are not callback-safe.
+    class Elem {
+     public:
+      Elem(const std::string& referrer, const std::string& referree, jlong size, jint length)
+          : referrer_(referrer), referree_(referree), size_(size), length_(length) {}
+      virtual ~Elem() {}
+
+      std::string Print() const {
+        return StringPrintf("%s --(%s)--> %s [size=%" PRId64 ", length=%d]",
+                            referrer_.c_str(),
+                            PrintArrowType().c_str(),
+                            referree_.c_str(),
+                            size_,
+                            length_);
+      }
+
+     protected:
+      virtual std::string PrintArrowType() const = 0;
+
+     private:
+      std::string referrer_;
+      std::string referree_;
+      jlong size_;
+      jint length_;
+    };
+
+    class JNILocalElement : public Elem {
+     public:
+      JNILocalElement(const std::string& referrer,
+                      const std::string& referree,
+                      jlong size,
+                      jint length,
+                      const jvmtiHeapReferenceInfo* reference_info)
+          : Elem(referrer, referree, size, length) {
+        memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo));
+      }
+
+     protected:
+      std::string PrintArrowType() const OVERRIDE {
+        char* name = nullptr;
+        if (info_.jni_local.method != nullptr) {
+          jvmti_env->GetMethodName(info_.jni_local.method, &name, nullptr, nullptr);
+        }
+        std::string ret = StringPrintf("jni-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
+                                       "method=%s]",
+                                       info_.jni_local.thread_id,
+                                       info_.jni_local.thread_tag,
+                                       info_.jni_local.depth,
+                                       name == nullptr ? "<null>" : name);
+        if (name != nullptr) {
+          jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+        }
+
+        return ret;
+      }
+
+     private:
+      const std::string string_;
+      jvmtiHeapReferenceInfo info_;
+    };
+
+    // For simple or unimplemented cases.
+    class StringElement : public Elem {
+     public:
+      StringElement(const std::string& referrer,
+                   const std::string& referree,
+                   jlong size,
+                   jint length,
+                   const std::string& string)
+          : Elem(referrer, referree, size, length), string_(string) {}
+
+     protected:
+      std::string PrintArrowType() const OVERRIDE {
+        return string_;
+      }
+
+     private:
+      const std::string string_;
+    };
+
+    static std::unique_ptr<Elem> CreateElem(const std::string& referrer,
+                                            const std::string& referree,
+                                            jvmtiHeapReferenceKind reference_kind,
+                                            const jvmtiHeapReferenceInfo* reference_info,
+                                            jlong size,
+                                            jint length) {
+      switch (reference_kind) {
+        case JVMTI_HEAP_REFERENCE_CLASS:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "class"));
+        case JVMTI_HEAP_REFERENCE_FIELD: {
+          std::string tmp = StringPrintf("field@%d", reference_info->field.index);
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                        referree,
+                                                        size,
+                                                        length,
+                                                        tmp));
+        }
+        case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: {
+          std::string tmp = StringPrintf("array-element@%d", reference_info->array.index);
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         tmp));
+        }
+        case JVMTI_HEAP_REFERENCE_CLASS_LOADER:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "classloader"));
+        case JVMTI_HEAP_REFERENCE_SIGNERS:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "signers"));
+        case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "protection-domain"));
+        case JVMTI_HEAP_REFERENCE_INTERFACE:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "interface"));
+        case JVMTI_HEAP_REFERENCE_STATIC_FIELD: {
+          std::string tmp = StringPrintf("array-element@%d", reference_info->array.index);
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         tmp));;
+        }
+        case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "constant-pool"));
+        case JVMTI_HEAP_REFERENCE_SUPERCLASS:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "superclass"));
+        case JVMTI_HEAP_REFERENCE_JNI_GLOBAL:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "jni-global"));
+        case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "system-class"));
+        case JVMTI_HEAP_REFERENCE_MONITOR:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "monitor"));
+        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "stack-local"));
+        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
+          return std::unique_ptr<Elem>(new JNILocalElement(referrer,
+                                                           referree,
+                                                           size,
+                                                           length,
+                                                           reference_info));
+        case JVMTI_HEAP_REFERENCE_THREAD:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "thread"));
+        case JVMTI_HEAP_REFERENCE_OTHER:
+          return std::unique_ptr<Elem>(new StringElement(referrer,
+                                                         referree,
+                                                         size,
+                                                         length,
+                                                         "other"));
+      }
+      LOG(FATAL) << "Unknown kind";
+      UNREACHABLE();
+    }
+
     static void DumpStacks() NO_THREAD_SAFETY_ANALYSIS {
       auto dump_function = [](art::Thread* t, void* data ATTRIBUTE_UNUSED) {
         std::string name;
@@ -209,58 +418,15 @@
       art::Runtime::Current()->GetThreadList()->ForEach(dump_function, nullptr);
     }
 
-    static std::string GetReferenceTypeStr(jvmtiHeapReferenceKind reference_kind,
-                                           const jvmtiHeapReferenceInfo* reference_info) {
-      switch (reference_kind) {
-        case JVMTI_HEAP_REFERENCE_CLASS:
-          return "class";
-        case JVMTI_HEAP_REFERENCE_FIELD:
-          return StringPrintf("field@%d", reference_info->field.index);
-        case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
-          return StringPrintf("array-element@%d", reference_info->array.index);
-        case JVMTI_HEAP_REFERENCE_CLASS_LOADER:
-          return "classloader";
-        case JVMTI_HEAP_REFERENCE_SIGNERS:
-          return "signers";
-        case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN:
-          return "protection-domain";
-        case JVMTI_HEAP_REFERENCE_INTERFACE:
-          return "interface";
-        case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
-          return StringPrintf("static-field@%d", reference_info->field.index);
-        case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
-          return "constant-pool";
-        case JVMTI_HEAP_REFERENCE_SUPERCLASS:
-          return "superclass";
-        case JVMTI_HEAP_REFERENCE_JNI_GLOBAL:
-          return "jni-global";
-        case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
-          return "system-class";
-        case JVMTI_HEAP_REFERENCE_MONITOR:
-          return "monitor";
-        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
-          return "stack-local";
-        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
-          return "jni-local";
-        case JVMTI_HEAP_REFERENCE_THREAD:
-          return "thread";
-        case JVMTI_HEAP_REFERENCE_OTHER:
-          return "other";
-      }
-      return "unknown";
-    }
-
-    const std::vector<std::string>& GetLines() const {
-      return lines_;
-    }
-
-   private:
     jint counter_;
     const jint stop_after_;
     const jint follow_set_;
-    std::vector<std::string> lines_;
+
+    std::vector<std::unique_ptr<Elem>> lines_;
   };
 
+  jit::ScopedJitSuspend sjs;  // Wait to avoid JIT influence (e.g., JNI globals).
+
   // If jniRef isn't null, add a local and a global ref.
   ScopedLocalRef<jobject> jni_local_ref(env, nullptr);
   jobject jni_global_ref = nullptr;
@@ -272,7 +438,7 @@
   PrintIterationConfig config(stop_after, follow_set);
   Run(heap_filter, klass_filter, initial_object, &config);
 
-  const std::vector<std::string>& lines = config.GetLines();
+  std::vector<std::string> lines = config.GetLines();
   jobjectArray ret = CreateObjectArray(env,
                                        static_cast<jint>(lines.size()),
                                        "java/lang/String",
@@ -299,12 +465,5 @@
   return 0;
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_waitForJitCompilation(JNIEnv*, jclass) {
-  jit::Jit* jit = Runtime::Current()->GetJit();
-  if (jit != nullptr) {
-    jit->WaitForCompilationToFinish(Thread::Current());
-  }
-}
-
 }  // namespace Test913Heaps
 }  // namespace art
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
index fc00ada..a6ace9a 100644
--- a/test/913-heaps/src/Main.java
+++ b/test/913-heaps/src/Main.java
@@ -101,8 +101,6 @@
 
   private static void doFollowReferencesTestImpl(A root, int stopAfter, int followSet,
       Object asRoot, Verifier v, String additionalEnabled) {
-    waitForJitCompilation();  // Wait to avoid JIT influence (e.g., JNI globals).
-
     String[] lines =
         followReferences(0, null, root, stopAfter, followSet, asRoot);
 
@@ -388,6 +386,4 @@
 
   private static native String[] followReferences(int heapFilter, Class<?> klassFilter,
       Object initialObject, int stopAfter, int followSet, Object jniRef);
-
-  private static native void waitForJitCompilation();
 }
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index 0a5caa1..9b09327 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -7,3 +7,11 @@
 String constructors done.
 testReferenceReturnValueConversions done.
 testPrimitiveReturnValueConversions done.
+Hi
+Hi
+Hi
+Hi
+Expect Hi here: Hi
+Don't expect Hi now
+[3, 2, 1]
+[1, 2, 3]
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index aab9f50..ee9c436 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -19,16 +19,20 @@
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.WrongMethodTypeException;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public class Main {
 
   public static class A {
+    public A() {}
+
     public void foo() {
       System.out.println("foo_A");
     }
@@ -65,11 +69,13 @@
     testfindSpecial_invokeDirectBehaviour();
     testExceptionDetailMessages();
     testfindVirtual();
+    testfindStatic();
     testUnreflects();
     testAsType();
     testConstructors();
     testStringConstructors();
     testReturnValueConversions();
+    testVariableArity();
   }
 
   public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -116,6 +122,19 @@
       System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded.");
     } catch (IllegalAccessException expected) {
     }
+
+    // Check return type matches for find.
+    try {
+      B.lookup.findSpecial(A.class /* refC */, "foo",
+                           MethodType.methodType(int.class), B.class /* specialCaller */);
+      fail();
+    } catch (NoSuchMethodException e) {}
+    // Check constructors
+    try {
+      B.lookup.findSpecial(A.class /* refC */, "<init>",
+                           MethodType.methodType(void.class), B.class /* specialCaller */);
+      fail();
+    } catch (NoSuchMethodException e) {}
   }
 
   public static void testfindSpecial_invokeDirectBehaviour() throws Throwable {
@@ -189,9 +208,20 @@
       return "bar";
     }
 
+    public String add(int x, int y) {
+      return Arrays.toString(new int[] { x, y });
+    }
+
     private String privateMethod() { return "privateMethod"; }
 
-    public static String staticMethod() { return null; }
+    public static String staticMethod() { return staticString; }
+
+    private static String staticString;
+
+    {
+      // Static constructor
+      staticString = Long.toString(System.currentTimeMillis());
+    }
 
     static final MethodHandles.Lookup lookup = MethodHandles.lookup();
   }
@@ -232,6 +262,21 @@
       System.out.println("Unexpected return value for BarImpl#foo: " + str);
     }
 
+    // Find virtual should check rtype.
+    try {
+      mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
+                                              MethodType.methodType(void.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
+
+    // And ptypes
+    mh = MethodHandles.lookup().findVirtual(
+        BarImpl.class, "add", MethodType.methodType(String.class, int.class, int.class));
+    try {
+      mh = MethodHandles.lookup().findVirtual(
+          BarImpl.class, "add", MethodType.methodType(String.class, Integer.class, int.class));
+    } catch (NoSuchMethodException e) {}
+
     // .. and their super-interfaces.
     mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
         MethodType.methodType(String.class));
@@ -272,6 +317,37 @@
     if (!"superPackageMethod".equals(str)) {
       System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
     }
+
+    try {
+      MethodHandles.lookup().findVirtual(BarImpl.class, "<init>",
+                                        MethodType.methodType(void.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
+  }
+
+  public static void testfindStatic() throws Throwable {
+    MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
+                                      MethodType.methodType(String.class));
+    try {
+      MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
+                                        MethodType.methodType(void.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
+    try {
+      MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
+                                        MethodType.methodType(String.class, int.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
+    try {
+      MethodHandles.lookup().findStatic(BarImpl.class, "<clinit>",
+                                        MethodType.methodType(void.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
+    try {
+      MethodHandles.lookup().findStatic(BarImpl.class, "<init>",
+                                        MethodType.methodType(void.class));
+      fail();
+    } catch (NoSuchMethodException e) {}
   }
 
   static class UnreflectTester {
@@ -481,6 +557,34 @@
     }
   }
 
+  public static void assertTrue(boolean value) {
+    if (!value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertFalse(boolean value) {
+    if (value) {
+      throw new AssertionError("assertTrue value: " + value);
+    }
+  }
+
+  public static void assertEquals(int i1, int i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+  }
+
+  public static void assertEquals(long i1, long i2) {
+    if (i1 == i2) { return; }
+    throw new AssertionError("assertEquals l1: " + i1 + ", l2: " + i2);
+  }
+
+  public static void assertEquals(Object o, Object p) {
+    if (o == p) { return; }
+    if (o != null && p != null && o.equals(p)) { return; }
+    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+  }
+
   public static void assertEquals(String s1, String s2) {
     if (s1 == s2) {
       return;
@@ -886,4 +990,480 @@
     testReferenceReturnValueConversions();
     testPrimitiveReturnValueConversions();
   }
+
+  public static class BaseVariableArityTester {
+    public String update(Float f0, Float... floats) {
+      return "base " + f0 + ", " + Arrays.toString(floats);
+    }
+  }
+
+  public static class VariableArityTester extends BaseVariableArityTester {
+    private String lastResult;
+
+    // Constructors
+    public VariableArityTester() {}
+    public VariableArityTester(boolean... booleans) { update(booleans); }
+    public VariableArityTester(byte... bytes) { update(bytes); }
+    public VariableArityTester(char... chars) { update(chars); }
+    public VariableArityTester(short... shorts) { update(shorts); }
+    public VariableArityTester(int... ints) { update(ints); }
+    public VariableArityTester(long... longs) { update(longs); }
+    public VariableArityTester(float... floats) { update(floats); }
+    public VariableArityTester(double... doubles) { update(doubles); }
+    public VariableArityTester(Float f0, Float... floats) { update(f0, floats); }
+    public VariableArityTester(String s0, String... strings) { update(s0, strings); }
+    public VariableArityTester(char c, Number... numbers) { update(c, numbers); }
+    @SafeVarargs
+    public VariableArityTester(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+      update(l0, lists);
+    }
+    public VariableArityTester(List l0, List... lists) { update(l0, lists); }
+
+    // Methods
+    public String update(boolean... booleans) { return lastResult = tally(booleans); }
+    public String update(byte... bytes) { return lastResult = tally(bytes); }
+    public String update(char... chars) { return lastResult = tally(chars); }
+    public String update(short... shorts) { return lastResult = tally(shorts); }
+    public String update(int... ints) {
+      lastResult = tally(ints);
+      return lastResult;
+    }
+    public String update(long... longs) { return lastResult = tally(longs); }
+    public String update(float... floats) { return lastResult = tally(floats); }
+    public String update(double... doubles) { return lastResult = tally(doubles); }
+    @Override
+    public String update(Float f0, Float... floats) { return lastResult = tally(f0, floats); }
+    public String update(String s0, String... strings) { return lastResult = tally(s0, strings); }
+    public String update(char c, Number... numbers) { return lastResult = tally(c, numbers); }
+    @SafeVarargs
+    public final String update(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+      lastResult = tally(l0, lists);
+      return lastResult;
+    }
+    public String update(List l0, List... lists) { return lastResult = tally(l0, lists); }
+
+    public String arrayMethod(Object[] o) {
+      return Arrays.deepToString(o);
+    }
+
+    public String lastResult() { return lastResult; }
+
+    // Static Methods
+    public static String tally(boolean... booleans) { return Arrays.toString(booleans); }
+    public static String tally(byte... bytes) { return Arrays.toString(bytes); }
+    public static String tally(char... chars) { return Arrays.toString(chars); }
+    public static String tally(short... shorts) { return Arrays.toString(shorts); }
+    public static String tally(int... ints) { return Arrays.toString(ints); }
+    public static String tally(long... longs) { return Arrays.toString(longs); }
+    public static String tally(float... floats) { return Arrays.toString(floats); }
+    public static String tally(double... doubles) { return Arrays.toString(doubles); }
+    public static String tally(Float f0, Float... floats) {
+      return f0 + ", " + Arrays.toString(floats);
+    }
+    public static String tally(String s0, String... strings) {
+      return s0 + ", " + Arrays.toString(strings);
+    }
+    public static String tally(char c, Number... numbers) {
+      return c + ", " + Arrays.toString(numbers);
+    }
+    @SafeVarargs
+    public static String tally(ArrayList<Integer> l0, ArrayList<Integer>... lists) {
+      return Arrays.toString(l0.toArray()) + ", " + Arrays.deepToString(lists);
+    }
+    public static String tally(List l0, List... lists) {
+      return Arrays.deepToString(l0.toArray()) + ", " + Arrays.deepToString(lists);
+    }
+    public static void foo(int... ints) { System.out.println(Arrays.toString(ints)); }
+    public static long sumToPrimitive(int... ints) {
+      long result = 0;
+      for (int i : ints) result += i;
+      return result;
+    }
+    public static Long sumToReference(int... ints) {
+      System.err.println("Hi");
+      return new Long(sumToPrimitive(ints));
+    }
+    public static MethodHandles.Lookup lookup() {
+      return MethodHandles.lookup();
+    }
+  }
+
+  // This method only exists to fool Jack's handling of types. See b/32536744.
+  public static Object getAsObject(String[] strings) {
+    return (Object) strings;
+  }
+
+  public static void testVariableArity() throws Throwable {
+    MethodHandle mh;
+    VariableArityTester vat = new VariableArityTester();
+
+    assertEquals("[1]", vat.update(1));
+    assertEquals("[1, 1]", vat.update(1, 1));
+    assertEquals("[1, 1, 1]", vat.update(1, 1, 1));
+
+    // Methods - boolean
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, boolean[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertFalse(mh.asFixedArity().isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[true, false, true]", mh.invoke(vat, true, false, true));
+    assertEquals("[true, false, true]", mh.invoke(vat, new boolean[] { true, false, true}));
+    assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), Boolean.valueOf(true)));
+    try {
+      mh.invoke(vat, true, true, 0);
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    try {
+      assertEquals("[false, true]", mh.invoke(vat, Boolean.valueOf(false), (Boolean) null));
+      fail();
+    } catch (NullPointerException e) {}
+
+    // Methods - byte
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, byte[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[32, 64, 97]", mh.invoke(vat, (byte) 32, Byte.valueOf((byte) 64), (byte) 97));
+    assertEquals("[32, 64, 97]", mh.invoke(vat, new byte[] {(byte) 32, (byte) 64, (byte) 97}));
+    try {
+      mh.invoke(vat, (byte) 1, Integer.valueOf(3), (byte) 0);
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Methods - char
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, char[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[A, B, C]", mh.invoke(vat, 'A', Character.valueOf('B'), 'C'));
+    assertEquals("[W, X, Y, Z]", mh.invoke(vat, new char[] { 'W', 'X', 'Y', 'Z' }));
+
+    // Methods - short
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, short[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[32767, -32768, 0]",
+                 mh.invoke(vat, Short.MAX_VALUE, Short.MIN_VALUE, Short.valueOf((short) 0)));
+    assertEquals("[1, -1]", mh.invoke(vat, new short[] { (short) 1, (short) -1 }));
+
+    // Methods - int
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, int[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[0, 2147483647, -2147483648, 0]",
+                 mh.invoke(vat, Integer.valueOf(0), Integer.MAX_VALUE, Integer.MIN_VALUE, 0));
+    assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new int[] { 0, -1, 1, 0 }));
+
+    assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, new int [] { 5, 4, 3, 2, 1 }));
+    try {
+      assertEquals("[5, 4, 3, 2, 1]", (String) mh.invokeExact(vat, 5, 4, 3, 2, 1));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    assertEquals("[5, 4, 3, 2, 1]", (String) mh.invoke(vat, 5, 4, 3, 2, 1));
+
+    // Methods - long
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, long[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[0, 9223372036854775807, -9223372036854775808]",
+                 mh.invoke(vat, Long.valueOf(0), Long.MAX_VALUE, Long.MIN_VALUE));
+    assertEquals("[0, -1, 1, 0]", mh.invoke(vat, new long[] { 0, -1, 1, 0 }));
+
+    // Methods - float
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, float[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[0.0, 1.25, -1.25]",
+                 mh.invoke(vat, 0.0f, Float.valueOf(1.25f), Float.valueOf(-1.25f)));
+    assertEquals("[0.0, -1.0, 1.0, 0.0]",
+                 mh.invoke(vat, new float[] { 0.0f, -1.0f, 1.0f, 0.0f }));
+
+    // Methods - double
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, double[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke(vat));
+    assertEquals("[0.0, 1.25, -1.25]",
+                 mh.invoke(vat, 0.0, Double.valueOf(1.25), Double.valueOf(-1.25)));
+    assertEquals("[0.0, -1.0, 1.0, 0.0]",
+                 mh.invoke(vat, new double[] { 0.0, -1.0, 1.0, 0.0 }));
+    mh.invoke(vat, 0.3f, 1.33, 1.33);
+
+    // Methods - String
+    mh = MethodHandles.lookup().
+        findVirtual(VariableArityTester.class, "update",
+                    MethodType.methodType(String.class, String.class, String[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("Echidna, []", mh.invoke(vat, "Echidna"));
+    assertEquals("Bongo, [Jerboa, Okapi]",
+                 mh.invoke(vat, "Bongo", "Jerboa", "Okapi"));
+
+    // Methods - Float
+    mh = MethodHandles.lookup().
+        findVirtual(VariableArityTester.class, "update",
+                    MethodType.methodType(String.class, Float.class, Float[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(vat, Float.valueOf(9.99f),
+                                    new Float[] { Float.valueOf(0.0f),
+                                                  Float.valueOf(0.1f),
+                                                  Float.valueOf(1.1f) }));
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f),
+                                    Float.valueOf(0.1f), Float.valueOf(1.1f)));
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+    try {
+      assertEquals("9.99, [77.0, 33.0, 64.0]",
+                   (String) mh.invoke(vat, Float.valueOf(9.99f), 77, 33, 64));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invokeExact(vat, Float.valueOf(9.99f),
+                                         new Float[] { Float.valueOf(0.0f),
+                                                       Float.valueOf(0.1f),
+                                                       Float.valueOf(1.1f) }));
+    assertEquals("9.99, [0.0, null, 1.1]",
+                 (String) mh.invokeExact(vat, Float.valueOf(9.99f),
+                                         new Float[] { Float.valueOf(0.0f),
+                                                       null,
+                                                       Float.valueOf(1.1f) }));
+    try {
+      assertEquals("9.99, [0.0, 0.1, 1.1]",
+                   (String) mh.invokeExact(vat, Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Methods - Number
+    mh = MethodHandles.lookup().
+        findVirtual(VariableArityTester.class, "update",
+                    MethodType.methodType(String.class, char.class, Number[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertFalse(mh.asFixedArity().isVarargsCollector());
+    assertEquals("x, []",  (String) mh.invoke(vat, 'x'));
+    assertEquals("x, [3.141]", (String) mh.invoke(vat, 'x', 3.141));
+    assertEquals("x, [null, 3.131, 37]",
+                 (String) mh.invoke(vat, 'x', null, 3.131, new Integer(37)));
+    try {
+      assertEquals("x, [null, 3.131, bad, 37]",
+                   (String) mh.invoke(vat, 'x', null, 3.131, "bad", new Integer(37)));
+      assertTrue(false);
+      fail();
+    } catch (ClassCastException e) {}
+    try {
+      assertEquals("x, [null, 3.131, bad, 37]",
+                   (String) mh.invoke(
+                       vat, 'x', (Process) null, 3.131, "bad", new Integer(37)));
+      assertTrue(false);
+      fail();
+    } catch (ClassCastException e) {}
+
+    // Methods - an array method that is not variable arity.
+    mh = MethodHandles.lookup().findVirtual(
+        VariableArityTester.class, "arrayMethod",
+        MethodType.methodType(String.class, Object[].class));
+    assertFalse(mh.isVarargsCollector());
+    mh.invoke(vat, new Object[] { "123" });
+    try {
+      assertEquals("-", mh.invoke(vat, new Float(3), new Float(4)));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    mh = mh.asVarargsCollector(Object[].class);
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[3.0, 4.0]", (String) mh.invoke(vat, new Float(3), new Float(4)));
+
+    // Constructors - default
+    mh = MethodHandles.lookup().findConstructor(
+        VariableArityTester.class, MethodType.methodType(void.class));
+    assertFalse(mh.isVarargsCollector());
+
+    // Constructors - boolean
+    mh = MethodHandles.lookup().findConstructor(
+        VariableArityTester.class, MethodType.methodType(void.class, boolean[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[true, true, false]",
+                 ((VariableArityTester) mh.invoke(new boolean[] {true, true, false})).lastResult());
+    assertEquals("[true, true, false]",
+                 ((VariableArityTester) mh.invoke(true, true, false)).lastResult());
+    try {
+      assertEquals("[true, true, false]",
+                   ((VariableArityTester) mh.invokeExact(true, true, false)).lastResult());
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Constructors - byte
+    mh = MethodHandles.lookup().findConstructor(
+        VariableArityTester.class, MethodType.methodType(void.class, byte[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[55, 66, 60]",
+                 ((VariableArityTester)
+                  mh.invoke(new byte[] {(byte) 55, (byte) 66, (byte) 60})).lastResult());
+    assertEquals("[55, 66, 60]",
+                 ((VariableArityTester) mh.invoke(
+                     (byte) 55, (byte) 66, (byte) 60)).lastResult());
+    try {
+      assertEquals("[55, 66, 60]",
+                   ((VariableArityTester) mh.invokeExact(
+                       (byte) 55, (byte) 66, (byte) 60)).lastResult());
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    try {
+      assertEquals("[3, 3]",
+                   ((VariableArityTester) mh.invoke(
+                       new Number[] { Byte.valueOf((byte) 3), (byte) 3})).lastResult());
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Constructors - String (have a different path than other reference types).
+    mh = MethodHandles.lookup().findConstructor(
+        VariableArityTester.class, MethodType.methodType(void.class, String.class, String[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("x, []", ((VariableArityTester) mh.invoke("x")).lastResult());
+    assertEquals("x, [y]", ((VariableArityTester) mh.invoke("x", "y")).lastResult());
+    assertEquals("x, [y, z]",
+                 ((VariableArityTester) mh.invoke("x", new String[] { "y", "z" })).lastResult());
+    try {
+      assertEquals("x, [y]", ((VariableArityTester) mh.invokeExact("x", "y")).lastResult());
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    assertEquals("x, [null, z]",
+                 ((VariableArityTester) mh.invoke("x", new String[] { null, "z" })).lastResult());
+
+    // Constructors - Number
+    mh = MethodHandles.lookup().findConstructor(
+        VariableArityTester.class, MethodType.methodType(void.class, char.class, Number[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertFalse(mh.asFixedArity().isVarargsCollector());
+    assertEquals("x, []", ((VariableArityTester) mh.invoke('x')).lastResult());
+    assertEquals("x, [3.141]", ((VariableArityTester) mh.invoke('x', 3.141)).lastResult());
+    assertEquals("x, [null, 3.131, 37]",
+                 ((VariableArityTester) mh.invoke('x', null, 3.131, new Integer(37))).lastResult());
+    try {
+      assertEquals("x, [null, 3.131, bad, 37]",
+                   ((VariableArityTester) mh.invoke(
+                       'x', null, 3.131, "bad", new Integer(37))).lastResult());
+      assertTrue(false);
+      fail();
+    } catch (ClassCastException e) {}
+    try {
+      assertEquals("x, [null, 3.131, bad, 37]",
+                   ((VariableArityTester) mh.invoke(
+                       'x', (Process) null, 3.131, "bad", new Integer(37))).lastResult());
+      assertTrue(false);
+      fail();
+    } catch (ClassCastException e) {}
+
+    // Static Methods - Float
+    mh = MethodHandles.lookup().
+        findStatic(VariableArityTester.class, "tally",
+                   MethodType.methodType(String.class, Float.class, Float[].class));
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(Float.valueOf(9.99f),
+                                    new Float[] { Float.valueOf(0.0f),
+                                                  Float.valueOf(0.1f),
+                                                  Float.valueOf(1.1f) }));
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(Float.valueOf(9.99f), Float.valueOf(0.0f),
+                                    Float.valueOf(0.1f), Float.valueOf(1.1f)));
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invoke(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+    try {
+      assertEquals("9.99, [77.0, 33.0, 64.0]",
+                   (String) mh.invoke(Float.valueOf(9.99f), 77, 33, 64));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    assertEquals("9.99, [0.0, 0.1, 1.1]",
+                 (String) mh.invokeExact(Float.valueOf(9.99f),
+                                         new Float[] { Float.valueOf(0.0f),
+                                                       Float.valueOf(0.1f),
+                                                       Float.valueOf(1.1f) }));
+    assertEquals("9.99, [0.0, null, 1.1]",
+                 (String) mh.invokeExact(Float.valueOf(9.99f),
+                                         new Float[] { Float.valueOf(0.0f),
+                                                       null,
+                                                       Float.valueOf(1.1f) }));
+    try {
+      assertEquals("9.99, [0.0, 0.1, 1.1]",
+                   (String) mh.invokeExact(Float.valueOf(9.99f), 0.0f, 0.1f, 1.1f));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Special methods - Float
+    mh = VariableArityTester.lookup().
+            findSpecial(BaseVariableArityTester.class, "update",
+                        MethodType.methodType(String.class, Float.class, Float[].class),
+                        VariableArityTester.class);
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("base 9.99, [0.0, 0.1, 1.1]",
+    (String) mh.invoke(vat,
+                       Float.valueOf(9.99f),
+                       new Float[] { Float.valueOf(0.0f),
+                                     Float.valueOf(0.1f),
+                                     Float.valueOf(1.1f) }));
+    assertEquals("base 9.99, [0.0, 0.1, 1.1]",
+    (String) mh.invoke(vat, Float.valueOf(9.99f), Float.valueOf(0.0f),
+                       Float.valueOf(0.1f), Float.valueOf(1.1f)));
+
+    // Return value conversions.
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, int[].class));
+    assertEquals("[1, 2, 3]", (String) mh.invoke(vat, 1, 2, 3));
+    assertEquals("[1, 2, 3]", (Object) mh.invoke(vat, 1, 2, 3));
+    try {
+      assertEquals("[1, 2, 3, 4]", (long) mh.invoke(vat, 1, 2, 3));
+      fail();
+    } catch (WrongMethodTypeException e) {}
+    assertEquals("[1, 2, 3]", vat.lastResult());
+    mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToPrimitive",
+                                           MethodType.methodType(long.class, int[].class));
+    assertEquals(10l, (long) mh.invoke(1, 2, 3, 4));
+    assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4));
+    mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "sumToReference",
+                                           MethodType.methodType(Long.class, int[].class));
+    Object o = mh.invoke(1, 2, 3, 4);
+    long l = (long) mh.invoke(1, 2, 3, 4);
+    assertEquals(10l, (long) mh.invoke(1, 2, 3, 4));
+    assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4));
+    try {
+      // WrongMethodTypeException should be raised before invoke here.
+      System.err.print("Expect Hi here: ");
+      assertEquals(Long.valueOf(10l), (Byte) mh.invoke(1, 2, 3, 4));
+      fail();
+    } catch (ClassCastException e) {}
+    try {
+      // WrongMethodTypeException should be raised before invoke here.
+      System.err.println("Don't expect Hi now");
+      byte b = (byte) mh.invoke(1, 2, 3, 4);
+      fail();
+    } catch (WrongMethodTypeException e) {}
+
+    // Return void produces 0 / null.
+    mh = MethodHandles.lookup().findStatic(VariableArityTester.class, "foo",
+                                           MethodType.methodType(void.class, int[].class));
+    assertEquals(null, (Object) mh.invoke(3, 2, 1));
+    assertEquals(0l, (long) mh.invoke(1, 2, 3));
+
+    // Combinators
+    mh = MethodHandles.lookup().findVirtual(VariableArityTester.class, "update",
+                                            MethodType.methodType(String.class, boolean[].class));
+    assertTrue(mh.isVarargsCollector());
+    mh = mh.bindTo(vat);
+    assertFalse(mh.isVarargsCollector());
+    mh = mh.asVarargsCollector(boolean[].class);
+    assertTrue(mh.isVarargsCollector());
+    assertEquals("[]", mh.invoke());
+    assertEquals("[true, false, true]", mh.invoke(true, false, true));
+    assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true}));
+    assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true)));
+    try {
+      mh.invoke(true, true, 0);
+      fail();
+    } catch (WrongMethodTypeException e) {}
+  }
 }
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 845545c..21b31b4 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -17,56 +17,35 @@
 # Known broken tests for the ARM VIXL backend.
 TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
   003-omnibus-opcodes \
-  004-ThreadStress \
-  028-array-write \
-  037-inherit \
+  020-string \
+  021-string2 \
   042-new-instance \
   044-proxy \
   080-oom-throw \
   082-inline-execute \
-  083-compiler-regressions \
   096-array-copy-concurrent-gc \
   099-vmdebug \
+  100-reflect2 \
   103-string-append \
   114-ParallelGC \
   122-npe \
-  123-inline-execute2 \
   129-ThreadGetId \
   137-cfi \
   144-static-field-sigquit \
-  201-built-in-except-detail-messages \
   412-new-array \
-  422-type-conversion \
-  437-inline \
   439-npe \
-  442-checker-constant-folding \
   450-checker-types \
-  458-checker-instruct-simplification \
-  458-long-to-fpu \
   488-checker-inline-recursive-calls \
-  510-checker-try-catch \
   515-dce-dominator \
   520-equivalent-phi \
   525-checker-arrays-fields1 \
   525-checker-arrays-fields2 \
   527-checker-array-access-split \
-  530-checker-loops2 \
-  530-checker-lse \
-  530-checker-lse2 \
-  535-regression-const-val \
-  536-checker-intrinsic-optimization \
   538-checker-embed-constants \
-  550-checker-multiply-accumulate \
-  552-checker-primitive-typeprop \
   552-checker-sharpening \
-  555-UnsafeGetLong-regression \
   562-checker-no-intermediate \
-  564-checker-negbitwise \
   570-checker-osr \
-  570-checker-select \
-  574-irreducible-and-constant-area \
-  580-checker-round \
-  594-checker-array-alias \
   602-deoptimizeable \
   700-LoadArgRegs \
   800-smali \
+
diff --git a/test/Android.bp b/test/Android.bp
index bdb7f80..fe20f29 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -266,10 +266,7 @@
 art_cc_test_library {
     name: "libtiagent",
     defaults: ["libtiagent-defaults"],
-    shared_libs: [
-        "libart",
-        "libopenjdkjvmti",
-    ],
+    shared_libs: ["libart"],
 }
 
 art_cc_test_library {
@@ -278,10 +275,7 @@
         "libtiagent-defaults",
         "art_debug_defaults",
     ],
-    shared_libs: [
-        "libartd",
-        "libopenjdkjvmtid",
-    ],
+    shared_libs: ["libartd"],
 }
 
 cc_defaults {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 60318a4..96b984d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -608,10 +608,7 @@
 TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
-# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
-#      more parallel moves on x86, thus some Checker assertions may fail.
-TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
-  484-checker-register-hints
+TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
 
 # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
 TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
@@ -791,6 +788,10 @@
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libnativebridgetest)
 endif
 
+# Also need libopenjdkjvmti.
+TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti
+TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmtid
+
 # All tests require the host executables. The tests also depend on the core images, but on
 # specific version depending on the compiler.
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
@@ -804,7 +805,9 @@
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libnativebridgetest) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION) \
-  $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION)
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) \
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmti$(ART_HOST_SHLIB_EXTENSION) \
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmtid$(ART_HOST_SHLIB_EXTENSION) \
 
 ifneq ($(HOST_PREFER_32_BIT),true)
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
@@ -817,7 +820,10 @@
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libnativebridgetest) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdk$(ART_HOST_SHLIB_EXTENSION) \
-  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION)
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkd$(ART_HOST_SHLIB_EXTENSION) \
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmti$(ART_HOST_SHLIB_EXTENSION) \
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libopenjdkjvmtid$(ART_HOST_SHLIB_EXTENSION) \
+
 endif
 
 # Create a rule to build and run a tests following the form:
diff --git a/test/DexToDexDecompiler/Main.java b/test/DexToDexDecompiler/Main.java
new file mode 100644
index 0000000..8f5075a
--- /dev/null
+++ b/test/DexToDexDecompiler/Main.java
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+  Main() {
+    // Will be quickened with RETURN_VOID_NO_BARRIER.
+  }
+
+  public static void main() {
+    Main m = new Main();
+    Object o = m;
+    // The call and field accesses will be quickened.
+    m.foo(m.a);
+
+    // The checkcast will be quickened.
+    m.foo(((Main)o).a);
+  }
+
+  int a;
+  void foo(int a) {}
+}
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 3cb1f23..c601e3e 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -139,8 +139,8 @@
         float f9, int i10, float f10);
 
     // Normal native
-    native static void stackArgsSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6,
-        int i7, int i8);
+    native static long getStackArgSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6,
+        int stack_arg);
 
     // Normal native
     static native double logD(double d);
@@ -273,8 +273,8 @@
         float f9, int i10, float f10);
 
     @FastNative
-    native static void stackArgsSignExtendedMips64_Fast(int i1, int i2, int i3, int i4, int i5, int i6,
-        int i7, int i8);
+    native static long getStackArgSignExtendedMips64_Fast(int i1, int i2, int i3, int i4, int i5, int i6,
+        int stack_arg);
 
     @FastNative
     static native double logD_Fast(double d);
@@ -316,10 +316,6 @@
         float f9, int i10, float f10);
 
     @CriticalNative
-    native static void stackArgsSignExtendedMips64_Critical(int i1, int i2, int i3, int i4, int i5, int i6,
-        int i7, int i8);
-
-    @CriticalNative
     static native double logD_Critical(double d);
     @CriticalNative
     static native float logF_Critical(float f);
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c525b2b..bb3a3ad 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -58,6 +58,7 @@
 ARGS=""
 EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
 DRY_RUN="n" # if y prepare to run the test but don't run it.
+TEST_VDEX="n"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -243,6 +244,9 @@
     elif [ "x$1" = "x--dry-run" ]; then
         DRY_RUN="y"
         shift
+    elif [ "x$1" = "x--vdex" ]; then
+        TEST_VDEX="y"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -444,6 +448,7 @@
 fi
 
 dex2oat_cmdline="true"
+vdex_cmdline="true"
 mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
 strip_cmdline="true"
 
@@ -473,6 +478,9 @@
     # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
     dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}"
   fi
+  if [ "$TEST_VDEX" = "y" ]; then
+    vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+  fi
 fi
 
 if [ "$STRIP_DEX" = "y" ]; then
@@ -513,6 +521,7 @@
 # Remove whitespace.
 dex2oat_cmdline=$(echo $dex2oat_cmdline)
 dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+vdex_cmdline=$(echo $vdex_cmdline)
 
 if [ "$HOST" = "n" ]; then
     adb root > /dev/null
@@ -553,6 +562,7 @@
              export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
              export PATH=$ANDROID_ROOT/bin:$PATH && \
              $dex2oat_cmdline && \
+             $vdex_cmdline && \
              $strip_cmdline && \
              $dalvikvm_cmdline"
 
@@ -626,7 +636,7 @@
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
-      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline"
+      echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
     fi
 
     cd $ANDROID_BUILD_TOP
@@ -634,6 +644,7 @@
     rm -rf ${DEX_LOCATION}/dalvik-cache/
     mkdir -p ${mkdir_locations} || exit 1
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+    $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
 
     # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
diff --git a/test/run-test b/test/run-test
index 37eefb3..ea9622a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -351,6 +351,9 @@
     elif [ "x$1" = "x--bisection-search" ]; then
         bisection_search="yes"
         shift
+    elif [ "x$1" = "x--vdex" ]; then
+        run_args="${run_args} --vdex"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -640,6 +643,7 @@
         echo "    --pic-test            Compile the test code position independent."
         echo "    --quiet               Don't print anything except failure messages"
         echo "    --bisection-search    Perform bisection bug search."
+        echo "    --vdex                Test using vdex as in input to dex2oat. Only works with --prebuild."
     ) 1>&2  # Direct to stderr so usage is not printed if --quiet is set.
     exit 1
 fi
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 27c2054..493eafb 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -48,7 +48,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-java-files-under, test)
 LOCAL_JAR_MANIFEST := test/manifest.txt
-LOCAL_STATIC_JAVA_LIBRARIES := ahat junit
+LOCAL_STATIC_JAVA_LIBRARIES := ahat junit-host
 LOCAL_IS_HOST_MODULE := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := ahat-tests
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 12e0338..2d26b48 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -19,7 +19,17 @@
   exit 1
 fi
 
-out_dir=${OUT_DIR-out}
+# Logic for setting out_dir from build/make/core/envsetup.mk:
+if [[ -z $OUT_DIR ]]; then
+  if [[ -z $OUT_DIR_COMMON_BASE ]]; then
+    out_dir=out
+  else
+    out_dir=${OUT_DIR_COMMON_BASE}/${PWD##*/}
+  fi
+else
+  out_dir=${OUT_DIR}
+fi
+
 java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
 common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target ${out_dir}/host/linux-x86/bin/jack"
 mode="target"
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
index a0658ec..c1cdf1e 100644
--- a/tools/dexfuzz/README
+++ b/tools/dexfuzz/README
@@ -4,7 +4,7 @@
 DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of
 subtle changes ("mutations") to a file to produce a new test case. These test cases
 can be used to test the various modes of execution available to ART (Interpreter,
-Quick compiler, Optimizing compiler) to check for bugs in these modes of execution.
+Optimizing compiler) to check for bugs in these modes of execution.
 This is done by differential testing - each test file is executed with each mode of
 execution, and any differences between the resulting outputs may be an indication of
 a bug in one of the modes.
@@ -53,17 +53,16 @@
 
 And also at least two of the following backends:
   --interpreter
-  --quick
   --optimizing
 
 Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use
 --allarm. Also in this case only one backend is needed, if i.e., you wanted to test
-ARM Quick Backend vs. ARM64 Quick Backend.
+ARM Optimizing Backend vs. ARM64 Optimizing Backend.
 
 Some legal examples:
-  --arm --quick --optimizing
-  --x86 --quick --optimizing --interpreter
-  --allarm --quick
+  --arm --optimizing --interpreter
+  --x86 --optimizing --interpreter
+  --allarm --optimizing
 
 Add in --device=<device name, e.g. device:generic> if you want to specify a device.
 Add in --execute-dir=<dir on device> if you want to specify an execution directory.
@@ -98,7 +97,6 @@
              those occurrences.
 Timed Out  - mutated files that timed out for one or more backends.
              Current timeouts are:
-               Quick - 5 seconds
                Optimizing - 5 seconds
                Intepreter - 30 seconds
               (use --short-timeouts to set all backends to 2 seconds.)
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
index b442b22..7d5476d 100644
--- a/tools/dexfuzz/src/dexfuzz/Options.java
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -61,7 +61,6 @@
   public static boolean executeOnHost;
   public static boolean noBootImage;
   public static boolean useInterpreter;
-  public static boolean useQuick;
   public static boolean useOptimizing;
   public static boolean useArchArm;
   public static boolean useArchArm64;
@@ -101,7 +100,6 @@
     Log.always("    --execute-class=<c>  : When executing, execute this class (default: Main)");
     Log.always("");
     Log.always("    --interpreter        : Include the Interpreter in comparisons");
-    Log.always("    --quick              : Include the Quick Compiler in comparisons");
     Log.always("    --optimizing         : Include the Optimizing Compiler in comparisons");
     Log.always("");
     Log.always("    --arm                : Include ARM backends in comparisons");
@@ -160,8 +158,6 @@
       skipHostVerify = true;
     } else if (flag.equals("interpreter")) {
       useInterpreter = true;
-    } else if (flag.equals("quick")) {
-      useQuick = true;
     } else if (flag.equals("optimizing")) {
       useOptimizing = true;
     } else if (flag.equals("arm")) {
@@ -423,18 +419,15 @@
       if (useInterpreter) {
         backends++;
       }
-      if (useQuick) {
-        backends++;
-      }
       if (useOptimizing) {
         backends++;
       }
       if (useArchArm && useArchArm64) {
-        // Could just be comparing quick-ARM versus quick-ARM64?
+        // Could just be comparing optimizing-ARM versus optimizing-ARM64?
         backends++;
       }
       if (backends < 2) {
-        Log.error("Not enough backends specified! Try --quick --interpreter!");
+        Log.error("Not enough backends specified! Try --optimizing --interpreter!");
         return false;
       }
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
index 72e36e8..84ed4c4 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (device.noBootImageAvailable()) {
       commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
deleted file mode 100644
index d9228ed..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
+++ /dev/null
@@ -1,39 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class Arm64QuickBackendExecutor extends Executor {
-
-  public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Quick ");
-    if (device.noBootImageAvailable()) {
-      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
index ded8cf9..26a5eea 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (device.noBootImageAvailable()) {
       commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
deleted file mode 100644
index 0eb35f7..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
+++ /dev/null
@@ -1,39 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class ArmQuickBackendExecutor extends Executor {
-
-  public ArmQuickBackendExecutor(BaseListener listener, Device device) {
-    super("ARM Quick Backend", 5, listener, Architecture.ARM, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
-    if (device.noBootImageAvailable()) {
-      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
index 72d43e7..883ff2a 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
deleted file mode 100644
index e7e5ff6..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class Mips64QuickBackendExecutor extends Executor {
-
-  public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  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);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
index 63f6858..b7babdc 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
deleted file mode 100644
index b262090..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class MipsQuickBackendExecutor extends Executor {
-
-  public MipsQuickBackendExecutor(BaseListener listener, Device device) {
-    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  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);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
index 5908a8b..1d62051 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -30,6 +30,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     if (Options.executeOnHost) {
       commandBuilder.append(device.getHostExecutionFlags()).append(" ");
     }
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
deleted file mode 100644
index 9e8039d..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.Options;
-import dexfuzz.listeners.BaseListener;
-
-public class X86QuickBackendExecutor extends Executor {
-
-  public X86QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86 Quick Backend", 5, listener, Architecture.X86, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  protected String constructCommand(String programName) {
-    StringBuilder commandBuilder = new StringBuilder();
-    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Quick ");
-    if (Options.executeOnHost) {
-      commandBuilder.append(device.getHostExecutionFlags()).append(" ");
-    }
-    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
-    commandBuilder.append(executeClass);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
index 28ff1a5..ad44259 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -29,6 +29,9 @@
   protected String constructCommand(String programName) {
     StringBuilder commandBuilder = new StringBuilder();
     commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    // The -Xno-dex-file-fallback option ensures that the execution does not default to
+    // interpreter if compilations fails.
+    commandBuilder.append("-Xno-dex-file-fallback ");
     commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
     commandBuilder.append(executeClass);
     return commandBuilder.toString();
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
deleted file mode 100644
index 22cafe2..0000000
--- a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
+++ /dev/null
@@ -1,36 +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.
- */
-
-package dexfuzz.executors;
-
-import dexfuzz.listeners.BaseListener;
-
-public class X86_64QuickBackendExecutor extends Executor {
-
-  public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
-    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device,
-        /*needsCleanCodeCache*/ true, /*isBisectable*/ false);
-  }
-
-  @Override
-  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);
-    return commandBuilder.toString();
-  }
-}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
index bc39d79..1797d90 100644
--- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -22,24 +22,18 @@
 import dexfuzz.executors.Architecture;
 import dexfuzz.executors.Arm64InterpreterExecutor;
 import dexfuzz.executors.Arm64OptimizingBackendExecutor;
-import dexfuzz.executors.Arm64QuickBackendExecutor;
 import dexfuzz.executors.ArmInterpreterExecutor;
 import dexfuzz.executors.ArmOptimizingBackendExecutor;
-import dexfuzz.executors.ArmQuickBackendExecutor;
 import dexfuzz.executors.Device;
 import dexfuzz.executors.Executor;
 import dexfuzz.executors.Mips64InterpreterExecutor;
 import dexfuzz.executors.Mips64OptimizingBackendExecutor;
-import dexfuzz.executors.Mips64QuickBackendExecutor;
 import dexfuzz.executors.MipsInterpreterExecutor;
 import dexfuzz.executors.MipsOptimizingBackendExecutor;
-import dexfuzz.executors.MipsQuickBackendExecutor;
 import dexfuzz.executors.X86InterpreterExecutor;
 import dexfuzz.executors.X86OptimizingBackendExecutor;
-import dexfuzz.executors.X86QuickBackendExecutor;
 import dexfuzz.executors.X86_64InterpreterExecutor;
 import dexfuzz.executors.X86_64OptimizingBackendExecutor;
-import dexfuzz.executors.X86_64QuickBackendExecutor;
 import dexfuzz.listeners.BaseListener;
 import dexfuzz.program.Mutation;
 import dexfuzz.program.Program;
@@ -121,18 +115,13 @@
     }
   }
 
-  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick,
-      Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) {
-    // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter.
+  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> optimizing,
+      Class<? extends Executor> interpreter) {
+    // NB: Currently OptimizingBackend MUST come immediately before same arch's Interpreter.
     // This is because intepreter execution relies on there being an OAT file already
     // created to produce correct debug information. Otherwise we will see
     // false-positive divergences.
     try {
-      if (Options.useQuick) {
-        Constructor<? extends Executor> constructor =
-            quick.getConstructor(BaseListener.class, Device.class);
-        executors.add(constructor.newInstance(listener, device));
-      }
       if (Options.useOptimizing) {
         Constructor<? extends Executor> constructor =
             optimizing.getConstructor(BaseListener.class, Device.class);
@@ -165,33 +154,33 @@
     }
 
     if (Options.useArchArm64) {
-      addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class,
-          Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, Arm64OptimizingBackendExecutor.class,
+          Arm64InterpreterExecutor.class);
     }
 
     if (Options.useArchArm) {
-      addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class,
-          ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class);
+      addExecutorsForArchitecture(device, ArmOptimizingBackendExecutor.class,
+          ArmInterpreterExecutor.class);
     }
 
     if (Options.useArchX86_64) {
-      addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class,
-          X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, X86_64OptimizingBackendExecutor.class,
+          X86_64InterpreterExecutor.class);
     }
 
     if (Options.useArchX86) {
-      addExecutorsForArchitecture(device, X86QuickBackendExecutor.class,
-          X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, X86OptimizingBackendExecutor.class,
+          X86InterpreterExecutor.class);
     }
 
     if (Options.useArchMips64) {
-      addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class,
-          Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class);
+      addExecutorsForArchitecture(device, Mips64OptimizingBackendExecutor.class,
+          Mips64InterpreterExecutor.class);
     }
 
     if (Options.useArchMips) {
-      addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class,
-          MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class);
+      addExecutorsForArchitecture(device, MipsOptimizingBackendExecutor.class,
+          MipsInterpreterExecutor.class);
     }
 
     // Add the first backend as the golden executor for self-divergence tests.