Merge "Fix OOM test case to handle more optimizations"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index d55f310..0dcefea 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -157,7 +157,8 @@
   -Wno-sign-promo \
   -Wno-unused-parameter \
   -Wstrict-aliasing \
-  -fstrict-aliasing
+  -fstrict-aliasing \
+  -fvisibility=protected
 
 ART_TARGET_CLANG_CFLAGS :=
 ART_TARGET_CLANG_CFLAGS_arm :=
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index dcc67c3..63f3e64 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -528,6 +528,7 @@
   kFixupLoad,        // Mostly for immediates.
   kFixupVLoad,       // FP load which *may* be pc-relative.
   kFixupCBxZ,        // Cbz, Cbnz.
+  kFixupTBxZ,        // Tbz, Tbnz.
   kFixupPushPop,     // Not really pc relative, but changes size based on args.
   kFixupCondBranch,  // Conditional branch
   kFixupT1Branch,    // Thumb1 Unconditional branch
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index d7ef6f0..3575ade 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -56,8 +56,11 @@
     return nullptr;
   }
   if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
-    last_value_ = kNoValue;  // Make bad.
-    return nullptr;
+    // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations.
+    if (!modifications_allowed_) {
+      last_value_ = kNoValue;  // Make bad.
+      return nullptr;
+    }
   }
   if (allocator == nullptr) {
     allocator = allocator_;
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index c06ff6f..1a38692 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -214,7 +214,7 @@
   static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u;
 
   uint32_t bbs_processed_;
-  uint32_t max_bbs_to_process_;
+  uint32_t max_bbs_to_process_;  // Doesn't apply after the main GVN has converged.
 
   // We have 32-bit last_value_ so that we can detect when we run out of value names, see Good().
   // We usually don't check Good() until the end of LVN unless we're about to modify code.
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index 5997568..8b02269 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -656,13 +656,37 @@
   }
 }
 
-void LocalValueNumbering::MergeNullChecked(const ValueNameSet::value_type& entry,
-                                           ValueNameSet::iterator hint) {
-  // Merge null_checked_ for this ref.
-  merge_names_.clear();
-  merge_names_.resize(gvn_->merge_lvns_.size(), entry);
-  if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
-    null_checked_.insert(hint, entry);
+void LocalValueNumbering::MergeNullChecked() {
+  DCHECK_GE(gvn_->merge_lvns_.size(), 2u);
+
+  // Find the LVN with the least entries in the set.
+  const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0];
+  for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
+    if (lvn->null_checked_.size() < least_entries_lvn->null_checked_.size()) {
+      least_entries_lvn = lvn;
+    }
+  }
+
+  // For each null-checked value name check if it's null-checked in all the LVNs.
+  for (const auto& value_name : least_entries_lvn->null_checked_) {
+    // Merge null_checked_ for this ref.
+    merge_names_.clear();
+    merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+    if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+      null_checked_.insert(null_checked_.end(), value_name);
+    }
+  }
+
+  // Now check if the least_entries_lvn has a null-check as the last insn.
+  const BasicBlock* least_entries_bb = gvn_->GetBasicBlock(least_entries_lvn->Id());
+  if (gvn_->HasNullCheckLastInsn(least_entries_bb, id_)) {
+    int s_reg = least_entries_bb->last_mir_insn->ssa_rep->uses[0];
+    uint32_t value_name = least_entries_lvn->GetSRegValueName(s_reg);
+    merge_names_.clear();
+    merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+    if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+      null_checked_.insert(value_name);
+    }
   }
 }
 
@@ -896,8 +920,7 @@
   IntersectSets<RangeCheckSet, &LocalValueNumbering::range_checked_>();
 
   // Merge null_checked_. We may later insert more, such as merged object field values.
-  MergeSets<ValueNameSet, &LocalValueNumbering::null_checked_,
-            &LocalValueNumbering::MergeNullChecked>();
+  MergeNullChecked();
 
   if (merge_type == kCatchMerge) {
     // Memory is clobbered. New memory version already created, don't merge aliasing locations.
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 855d66d..f6a454b 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -343,11 +343,11 @@
                                      EscapedIFieldClobberSet::iterator hint);
   void MergeEscapedArrayClobberSets(const EscapedArrayClobberSet::value_type& entry,
                                     EscapedArrayClobberSet::iterator hint);
-  void MergeNullChecked(const ValueNameSet::value_type& entry, ValueNameSet::iterator hint);
   void MergeSFieldValues(const SFieldToValueMap::value_type& entry,
                          SFieldToValueMap::iterator hint);
   void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry,
                                     IFieldLocToValueMap::iterator hint);
+  void MergeNullChecked();
 
   template <typename Map, Map LocalValueNumbering::*map_ptr, typename Versions>
   void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint);
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index dee1361..16b529a 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <queue>
 
+#include "base/bit_vector-inl.h"
 #include "base/stl_util.h"
 #include "compiler_internals.h"
 #include "dex_file-inl.h"
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index c7dd85c..6658848 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "base/bit_vector-inl.h"
 #include "compiler_internals.h"
 #include "global_value_numbering.h"
 #include "local_value_numbering.h"
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index a449cbd..d001dd6 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -116,6 +116,7 @@
 #define IS_SIGNED_IMM7(value) IS_SIGNED_IMM(7, value)
 #define IS_SIGNED_IMM9(value) IS_SIGNED_IMM(9, value)
 #define IS_SIGNED_IMM12(value) IS_SIGNED_IMM(12, value)
+#define IS_SIGNED_IMM14(value) IS_SIGNED_IMM(14, value)
 #define IS_SIGNED_IMM19(value) IS_SIGNED_IMM(19, value)
 #define IS_SIGNED_IMM21(value) IS_SIGNED_IMM(21, value)
 
@@ -355,7 +356,10 @@
   kA64Sub4rrro,      // sub [s1001011000] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
   kA64Sub4RRre,      // sub [s1001011001] rm[20-16] option[15-13] imm_3[12-10] rn[9-5] rd[4-0].
   kA64Subs3rRd,      // subs[s111000100] imm_12[21-10] rn[9-5] rd[4-0].
+  kA64Tst2rl,        // tst alias of "ands rzr, rn, #imm".
   kA64Tst3rro,       // tst alias of "ands rzr, arg1, arg2, arg3".
+  kA64Tbnz3rht,      // tbnz imm_6_b5[31] [0110111] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
+  kA64Tbz3rht,       // tbz imm_6_b5[31] [0110110] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
   kA64Ubfm4rrdd,     // ubfm[s10100110] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
   kA64Last,
   kA64NotWide = 0,   // Flag used to select the first instruction variant.
@@ -400,23 +404,24 @@
 enum ArmEncodingKind {
   // All the formats below are encoded in the same way (as a kFmtBitBlt).
   // These are grouped together, for fast handling (e.g. "if (LIKELY(fmt <= kFmtBitBlt)) ...").
-  kFmtRegW = 0,  // Word register (w) or wzr.
-  kFmtRegX,      // Extended word register (x) or xzr.
-  kFmtRegR,      // Register with same width as the instruction or zr.
-  kFmtRegWOrSp,  // Word register (w) or wsp.
-  kFmtRegXOrSp,  // Extended word register (x) or sp.
-  kFmtRegROrSp,  // Register with same width as the instruction or sp.
-  kFmtRegS,      // Single FP reg.
-  kFmtRegD,      // Double FP reg.
-  kFmtRegF,      // Single/double FP reg depending on the instruction width.
-  kFmtBitBlt,    // Bit string using end/start.
+  kFmtRegW = 0,   // Word register (w) or wzr.
+  kFmtRegX,       // Extended word register (x) or xzr.
+  kFmtRegR,       // Register with same width as the instruction or zr.
+  kFmtRegWOrSp,   // Word register (w) or wsp.
+  kFmtRegXOrSp,   // Extended word register (x) or sp.
+  kFmtRegROrSp,   // Register with same width as the instruction or sp.
+  kFmtRegS,       // Single FP reg.
+  kFmtRegD,       // Double FP reg.
+  kFmtRegF,       // Single/double FP reg depending on the instruction width.
+  kFmtBitBlt,     // Bit string using end/start.
 
   // Less likely formats.
-  kFmtUnused,    // Unused field and marks end of formats.
-  kFmtImm21,     // Sign-extended immediate using [23..5,30..29].
-  kFmtShift,     // Register shift, 9-bit at [23..21, 15..10]..
-  kFmtExtend,    // Register extend, 9-bit at [23..21, 15..10].
-  kFmtSkip,      // Unused field, but continue to next.
+  kFmtUnused,     // Unused field and marks end of formats.
+  kFmtImm6Shift,  // Shift immediate, 6-bit at [31, 23..19].
+  kFmtImm21,      // Sign-extended immediate using [23..5,30..29].
+  kFmtShift,      // Register shift, 9-bit at [23..21, 15..10]..
+  kFmtExtend,     // Register extend, 9-bit at [23..21, 15..10].
+  kFmtSkip,       // Unused field, but continue to next.
 };
 
 // Struct used to define the snippet positions for each A64 opcode.
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 15c89f2..5115246 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -89,6 +89,7 @@
  *     M -> 16-bit shift expression ("" or ", lsl #16" or ", lsl #32"...)
  *     B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
  *     H -> operand shift
+ *     h -> 6-bit shift immediate
  *     T -> register shift (either ", lsl #0" or ", lsl #12")
  *     e -> register extend (e.g. uxtb #1)
  *     o -> register shift (e.g. lsl #1) for Word registers
@@ -614,10 +615,24 @@
                  kFmtRegR, 4, 0, kFmtRegROrSp, 9, 5, kFmtBitBlt, 21, 10,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
                  "subs", "!0r, !1R, #!2d", kFixupNone),
-    ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a000000),
+    ENCODING_MAP(WIDE(kA64Tst2rl), SF_VARIANTS(0x7200001f),
+                 kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | SETS_CCODES,
+                 "tst", "!0r, !1l", kFixupNone),
+    ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a00001f),
                  kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtShift, -1, -1,
-                 kFmtUnused, -1, -1, IS_QUAD_OP | REG_USE01 | SETS_CCODES,
+                 kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
                  "tst", "!0r, !1r!2o", kFixupNone),
+    // NOTE: Tbz/Tbnz does not require SETS_CCODES, but it may be replaced by some other LIRs
+    // which require SETS_CCODES in the fix-up stage.
+    ENCODING_MAP(WIDE(kA64Tbnz3rht), CUSTOM_VARIANTS(0x37000000, 0x37000000),
+                 kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+                 "tbnz", "!0r, #!1h, !2t", kFixupTBxZ),
+    ENCODING_MAP(WIDE(kA64Tbz3rht), CUSTOM_VARIANTS(0x36000000, 0x36000000),
+                 kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+                 IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+                 "tbz", "!0r, #!1h, !2t", kFixupTBxZ),
     ENCODING_MAP(WIDE(kA64Ubfm4rrdd), SF_N_VARIANTS(0x53000000),
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 21, 16,
                  kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE1,
@@ -787,6 +802,11 @@
               value |= ((operand & 0x1ffffc) >> 2) << 5;
               bits |= value;
               break;
+            case kFmtImm6Shift:
+              value = (operand & 0x1f) << 19;
+              value |= ((operand & 0x20) >> 5) << 31;
+              bits |= value;
+              break;
             default:
               LOG(FATAL) << "Bad fmt for arg. " << i << " in " << encoder->name
                          << " (" << kind << ")";
@@ -827,11 +847,6 @@
    */
   int generation = 0;
   while (true) {
-    // TODO(Arm64): check whether passes and offset adjustments are really necessary.
-    //   Currently they aren't, as - in the fixups below - LIR are never inserted.
-    //   Things can be different if jump ranges above 1 MB need to be supported.
-    //   If they are not, then we can get rid of the assembler retry logic.
-
     offset_adjustment = 0;
     AssemblerStatus res = kSuccess;  // Assume success
     generation ^= 1;
@@ -839,13 +854,9 @@
     lir = first_fixup_;
     prev_lir = NULL;
     while (lir != NULL) {
-      /*
-       * NOTE: the lir being considered here will be encoded following the switch (so long as
-       * we're not in a retry situation).  However, any new non-pc_rel instructions inserted
-       * due to retry must be explicitly encoded at the time of insertion.  Note that
-       * inserted instructions don't need use/def flags, but do need size and pc-rel status
-       * properly updated.
-       */
+      // NOTE: Any new non-pc_rel instructions inserted due to retry must be explicitly encoded at
+      // the time of insertion.  Note that inserted instructions don't need use/def flags, but do
+      // need size and pc-rel status properly updated.
       lir->offset += offset_adjustment;
       // During pass, allows us to tell whether a node has been updated with offset_adjustment yet.
       lir->flags.generation = generation;
@@ -861,7 +872,8 @@
           CodeOffset target = target_lir->offset +
               ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
           int32_t delta = target - pc;
-          if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+          DCHECK_EQ(delta & 0x3, 0);
+          if (!IS_SIGNED_IMM19(delta >> 2)) {
             LOG(FATAL) << "Invalid jump range in kFixupT1Branch";
           }
           lir->operands[0] = delta >> 2;
@@ -876,12 +888,75 @@
           CodeOffset target = target_lir->offset +
             ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
           int32_t delta = target - pc;
-          if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+          DCHECK_EQ(delta & 0x3, 0);
+          if (!IS_SIGNED_IMM19(delta >> 2)) {
             LOG(FATAL) << "Invalid jump range in kFixupLoad";
           }
           lir->operands[1] = delta >> 2;
           break;
         }
+        case kFixupTBxZ: {
+          int16_t opcode = lir->opcode;
+          RegStorage reg(lir->operands[0] | RegStorage::kValid);
+          int32_t imm = lir->operands[1];
+          DCHECK_EQ(IS_WIDE(opcode), reg.Is64Bit());
+          DCHECK_LT(imm, 64);
+          if (imm >= 32) {
+            DCHECK(IS_WIDE(opcode));
+          } else if (kIsDebugBuild && IS_WIDE(opcode)) {
+            // "tbz/tbnz x0, #imm(<32)" is the same with "tbz/tbnz w0, #imm(<32)", but GCC/oatdump
+            // will disassemble it as "tbz/tbnz w0, #imm(<32)". So unwide the LIR to make the
+            // compiler log behave the same with those disassembler in debug build.
+            // This will also affect tst instruction if it need to be replaced, but there is no
+            // performance difference between "tst Xt" and "tst Wt".
+            lir->opcode = UNWIDE(opcode);
+            lir->operands[0] = As32BitReg(reg).GetReg();
+          }
+
+          // Fix-up branch offset.
+          LIR *target_lir = lir->target;
+          DCHECK(target_lir);
+          CodeOffset pc = lir->offset;
+          CodeOffset target = target_lir->offset +
+              ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+          int32_t delta = target - pc;
+          DCHECK_EQ(delta & 0x3, 0);
+          // Check if branch offset can be encoded in tbz/tbnz.
+          if (!IS_SIGNED_IMM14(delta >> 2)) {
+            DexOffset dalvik_offset = lir->dalvik_offset;
+            int16_t opcode = lir->opcode;
+            LIR* target = lir->target;
+            // "tbz/tbnz Rt, #imm, label" -> "tst Rt, #(1<<imm)".
+            offset_adjustment -= lir->flags.size;
+            int32_t imm = EncodeLogicalImmediate(IS_WIDE(opcode), 1 << lir->operands[1]);
+            DCHECK_NE(imm, -1);
+            lir->opcode = IS_WIDE(opcode) ? WIDE(kA64Tst2rl) : kA64Tst2rl;
+            lir->operands[1] = imm;
+            lir->target = nullptr;
+            lir->flags.fixup = EncodingMap[kA64Tst2rl].fixup;
+            lir->flags.size = EncodingMap[kA64Tst2rl].size;
+            offset_adjustment += lir->flags.size;
+            // Insert "beq/bneq label".
+            opcode = UNWIDE(opcode);
+            DCHECK(opcode == kA64Tbz3rht || opcode == kA64Tbnz3rht);
+            LIR* new_lir = RawLIR(dalvik_offset, kA64B2ct,
+                opcode == kA64Tbz3rht ? kArmCondEq : kArmCondNe, 0, 0, 0, 0, target);
+            InsertLIRAfter(lir, new_lir);
+            new_lir->offset = lir->offset + lir->flags.size;
+            new_lir->flags.generation = generation;
+            new_lir->flags.fixup = EncodingMap[kA64B2ct].fixup;
+            new_lir->flags.size = EncodingMap[kA64B2ct].size;
+            offset_adjustment += new_lir->flags.size;
+            // lir no longer pcrel, unlink and link in new_lir.
+            ReplaceFixup(prev_lir, lir, new_lir);
+            prev_lir = new_lir;  // Continue with the new instruction.
+            lir = new_lir->u.a.pcrel_next;
+            res = kRetryAll;
+            continue;
+          }
+          lir->operands[2] = delta >> 2;
+          break;
+        }
         case kFixupAdr: {
           LIR* target_lir = lir->target;
           int32_t delta;
@@ -910,6 +985,7 @@
     }
 
     if (res == kSuccess) {
+      DCHECK_EQ(offset_adjustment, 0);
       break;
     } else {
       assembler_retries++;
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 0538c31..eddc3a3 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -208,9 +208,9 @@
   OpRegRegImm(kOpAdd, rs_x2, rs_x0, mirror::Object::MonitorOffset().Int32Value());
   NewLIR2(kA64Ldxr2rX, rw3, rx2);
   MarkPossibleNullPointerException(opt_flags);
-  LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_x1, 0, NULL);
+  LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_w3, 0, NULL);
   NewLIR3(kA64Stxr3wrX, rw3, rw1, rx2);
-  LIR* lock_success_branch = OpCmpImmBranch(kCondEq, rs_x1, 0, NULL);
+  LIR* lock_success_branch = OpCmpImmBranch(kCondEq, rs_w3, 0, NULL);
 
   LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
   not_unlocked_branch->target = slow_path_target;
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index d00c57d..d1b9c81 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -271,8 +271,12 @@
       ArmOpcode opcode = kA64Cbz2rt;
       ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
       branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
+    } else if (arm_cond == kArmCondLt || arm_cond == kArmCondGe) {
+      ArmOpcode opcode = (arm_cond == kArmCondLt) ? kA64Tbnz3rht : kA64Tbz3rht;
+      ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+      int value = reg.Is64Bit() ? 63 : 31;
+      branch = NewLIR3(opcode | wide, reg.GetReg(), value, 0);
     }
-    // TODO: Use tbz/tbnz for < 0 or >= 0.
   }
 
   if (branch == nullptr) {
@@ -856,16 +860,14 @@
   OpRegRegImm(kOpLsl, rs_length, rs_length, 1);
 
   // Copy one element.
-  OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 2);
-  LIR* jmp_to_copy_two = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+  LIR* jmp_to_copy_two = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 1, 0);
   OpRegImm(kOpSub, rs_length, 2);
   LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, kSignedHalf);
   StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, kSignedHalf);
 
   // Copy two elements.
   LIR *copy_two = NewLIR0(kPseudoTargetLabel);
-  OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 4);
-  LIR* jmp_to_copy_four = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+  LIR* jmp_to_copy_four = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 2, 0);
   OpRegImm(kOpSub, rs_length, 4);
   LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k32);
   StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k32);
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 9b4546a..685f8d5 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -504,6 +504,9 @@
              else
                strcpy(tbuf, ", DecodeError3");
              break;
+           case 'h':
+             snprintf(tbuf, arraysize(tbuf), "%d", operand);
+             break;
            default:
              strcpy(tbuf, "DecodeError1");
              break;
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index f4bb40c..833052d 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -1329,10 +1329,12 @@
       if (!cu_->target64 && rl_start.location != kLocPhysReg) {
         // Load the start index from stack, remembering that we pushed EDI.
         int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
-        {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          Load32Disp(rs_rX86_SP, displacement, rs_rDI);
-        }
+        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+        Load32Disp(rs_rX86_SP, displacement, rs_rDI);
+        // Dalvik register annotation in LoadBaseIndexedDisp() used wrong offset. Fix it.
+        DCHECK(!DECODE_ALIAS_INFO_WIDE(last_lir_insn_->flags.alias_info));
+        int reg_id = DECODE_ALIAS_INFO_REG(last_lir_insn_->flags.alias_info) - 1;
+        AnnotateDalvikRegAccess(last_lir_insn_, reg_id, true, false);
       } else {
         LoadValueDirectFixed(rl_start, rs_rDI);
       }
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index e26745a..4a55de6 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "base/bit_vector-inl.h"
 #include "compiler_internals.h"
 #include "dataflow_iterator-inl.h"
 #include "utils/scoped_arena_containers.h"
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index deefdec..a21004c 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -48,8 +48,17 @@
 
 class JniCompilerTest : public CommonCompilerTest {
  protected:
+  void SetUp() OVERRIDE {
+    CommonCompilerTest::SetUp();
+    check_generic_jni_ = false;
+  }
+
+  void SetCheckGenericJni(bool generic) {
+    check_generic_jni_ = generic;
+  }
+
   void CompileForTest(jobject class_loader, bool direct,
-                      const char* method_name, const char* method_sig, bool generic = false) {
+                      const char* method_name, const char* method_sig) {
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::ClassLoader> loader(
@@ -63,7 +72,7 @@
       method = c->FindVirtualMethod(method_name, method_sig);
     }
     ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
-    if (generic) {
+    if (check_generic_jni_) {
       method->SetEntryPointFromQuickCompiledCode(class_linker_->GetQuickGenericJniTrampoline());
     } else {
       if (method->GetEntryPointFromQuickCompiledCode() == nullptr ||
@@ -78,14 +87,14 @@
   }
 
   void SetUpForTest(bool direct, const char* method_name, const char* method_sig,
-                    void* native_fnptr, bool generic = false) {
+                    void* native_fnptr) {
     // Initialize class loader and compile method when runtime not started.
     if (!runtime_->IsStarted()) {
       {
         ScopedObjectAccess soa(Thread::Current());
         class_loader_ = LoadDex("MyClassNatives");
       }
-      CompileForTest(class_loader_, direct, method_name, method_sig, generic);
+      CompileForTest(class_loader_, direct, method_name, method_sig);
       // Start runtime.
       Thread::Current()->TransitionFromSuspendedToRunnable();
       bool started = runtime_->Start();
@@ -121,16 +130,65 @@
   static jobject jobj_;
   static jobject class_loader_;
 
-
  protected:
+  // We have to list the methods here so we can share them between default and generic JNI.
+  void CompileAndRunNoArgMethodImpl();
+  void CompileAndRunIntMethodThroughStubImpl();
+  void CompileAndRunStaticIntMethodThroughStubImpl();
+  void CompileAndRunIntMethodImpl();
+  void CompileAndRunIntIntMethodImpl();
+  void CompileAndRunLongLongMethodImpl();
+  void CompileAndRunDoubleDoubleMethodImpl();
+  void CompileAndRun_fooJJ_synchronizedImpl();
+  void CompileAndRunIntObjectObjectMethodImpl();
+  void CompileAndRunStaticIntIntMethodImpl();
+  void CompileAndRunStaticDoubleDoubleMethodImpl();
+  void RunStaticLogDoubleMethodImpl();
+  void RunStaticLogFloatMethodImpl();
+  void RunStaticReturnTrueImpl();
+  void RunStaticReturnFalseImpl();
+  void RunGenericStaticReturnIntImpl();
+  void CompileAndRunStaticIntObjectObjectMethodImpl();
+  void CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl();
+  void ExceptionHandlingImpl();
+  void NativeStackTraceElementImpl();
+  void ReturnGlobalRefImpl();
+  void LocalReferenceTableClearingTestImpl();
+  void JavaLangSystemArrayCopyImpl();
+  void CompareAndSwapIntImpl();
+  void GetTextImpl();
+  void GetSinkPropertiesNativeImpl();
+  void UpcallReturnTypeChecking_InstanceImpl();
+  void UpcallReturnTypeChecking_StaticImpl();
+  void UpcallArgumentTypeChecking_InstanceImpl();
+  void UpcallArgumentTypeChecking_StaticImpl();
+  void CompileAndRunFloatFloatMethodImpl();
+  void CheckParameterAlignImpl();
+  void MaxParamNumberImpl();
+  void WithoutImplementationImpl();
+  void StackArgsIntsFirstImpl();
+  void StackArgsFloatsFirstImpl();
+  void StackArgsMixedImpl();
+
   JNIEnv* env_;
   jmethodID jmethod_;
+  bool check_generic_jni_;
 };
 
 jclass JniCompilerTest::jklass_;
 jobject JniCompilerTest::jobj_;
 jobject JniCompilerTest::class_loader_;
 
+#define JNI_TEST(TestName) \
+  TEST_F(JniCompilerTest, TestName ## Default) { \
+    TestName ## Impl();                          \
+  }                                              \
+                                                 \
+  TEST_F(JniCompilerTest, TestName ## Generic) { \
+    TEST_DISABLED_FOR_MIPS();                    \
+    SetCheckGenericJni(true);                    \
+    TestName ## Impl();                          \
+  }
 
 int gJava_MyClassNatives_foo_calls = 0;
 void Java_MyClassNatives_foo(JNIEnv* env, jobject thisObj) {
@@ -145,7 +203,7 @@
   EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
 }
 
-TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
+void JniCompilerTest::CompileAndRunNoArgMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
 
@@ -154,9 +212,13 @@
   EXPECT_EQ(1, gJava_MyClassNatives_foo_calls);
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(2, gJava_MyClassNatives_foo_calls);
+
+  gJava_MyClassNatives_foo_calls = 0;
 }
 
-TEST_F(JniCompilerTest, CompileAndRunIntMethodThroughStub) {
+JNI_TEST(CompileAndRunNoArgMethod)
+
+void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "bar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_bar
@@ -169,7 +231,9 @@
   EXPECT_EQ(25, result);
 }
 
-TEST_F(JniCompilerTest, CompileAndRunStaticIntMethodThroughStub) {
+JNI_TEST(CompileAndRunIntMethodThroughStub)
+
+void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "sbar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_sbar
@@ -182,6 +246,8 @@
   EXPECT_EQ(43, result);
 }
 
+JNI_TEST(CompileAndRunStaticIntMethodThroughStub)
+
 int gJava_MyClassNatives_fooI_calls = 0;
 jint Java_MyClassNatives_fooI(JNIEnv* env, jobject thisObj, jint x) {
   // 1 = thisObj
@@ -195,7 +261,7 @@
   return x;
 }
 
-TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
+void JniCompilerTest::CompileAndRunIntMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooI));
@@ -207,8 +273,12 @@
   result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFED00D);
   EXPECT_EQ(static_cast<jint>(0xCAFED00D), result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooI_calls);
+
+  gJava_MyClassNatives_fooI_calls = 0;
 }
 
+JNI_TEST(CompileAndRunIntMethod)
+
 int gJava_MyClassNatives_fooII_calls = 0;
 jint Java_MyClassNatives_fooII(JNIEnv* env, jobject thisObj, jint x, jint y) {
   // 1 = thisObj
@@ -222,7 +292,7 @@
   return x - y;  // non-commutative operator
 }
 
-TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
+void JniCompilerTest::CompileAndRunIntIntMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooII", "(II)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooII));
@@ -235,8 +305,12 @@
                                          0xCAFED00D);
   EXPECT_EQ(static_cast<jint>(0xCAFEBABE - 0xCAFED00D), result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooII_calls);
+
+  gJava_MyClassNatives_fooII_calls = 0;
 }
 
+JNI_TEST(CompileAndRunIntIntMethod)
+
 int gJava_MyClassNatives_fooJJ_calls = 0;
 jlong Java_MyClassNatives_fooJJ(JNIEnv* env, jobject thisObj, jlong x, jlong y) {
   // 1 = thisObj
@@ -250,7 +324,7 @@
   return x - y;  // non-commutative operator
 }
 
-TEST_F(JniCompilerTest, CompileAndRunLongLongMethod) {
+void JniCompilerTest::CompileAndRunLongLongMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooJJ", "(JJ)J",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ));
@@ -264,8 +338,12 @@
   result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, b, a);
   EXPECT_EQ(b - a, result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooJJ_calls);
+
+  gJava_MyClassNatives_fooJJ_calls = 0;
 }
 
+JNI_TEST(CompileAndRunLongLongMethod)
+
 int gJava_MyClassNatives_fooDD_calls = 0;
 jdouble Java_MyClassNatives_fooDD(JNIEnv* env, jobject thisObj, jdouble x, jdouble y) {
   // 1 = thisObj
@@ -279,7 +357,7 @@
   return x - y;  // non-commutative operator
 }
 
-TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
+void JniCompilerTest::CompileAndRunDoubleDoubleMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooDD", "(DD)D",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooDD));
@@ -294,6 +372,8 @@
   result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_, a, b);
   EXPECT_EQ(a - b, result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooDD_calls);
+
+  gJava_MyClassNatives_fooDD_calls = 0;
 }
 
 int gJava_MyClassNatives_fooJJ_synchronized_calls = 0;
@@ -309,7 +389,7 @@
   return x | y;
 }
 
-TEST_F(JniCompilerTest, CompileAndRun_fooJJ_synchronized) {
+void JniCompilerTest::CompileAndRun_fooJJ_synchronizedImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooJJ_synchronized", "(JJ)J",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ_synchronized));
@@ -320,8 +400,12 @@
   jlong result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, a, b);
   EXPECT_EQ(a | b, result);
   EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_synchronized_calls);
+
+  gJava_MyClassNatives_fooJJ_synchronized_calls = 0;
 }
 
+JNI_TEST(CompileAndRun_fooJJ_synchronized)
+
 int gJava_MyClassNatives_fooIOO_calls = 0;
 jobject Java_MyClassNatives_fooIOO(JNIEnv* env, jobject thisObj, jint x, jobject y,
                             jobject z) {
@@ -345,7 +429,7 @@
   }
 }
 
-TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
+void JniCompilerTest::CompileAndRunIntObjectObjectMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
@@ -375,8 +459,12 @@
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, jklass_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
   EXPECT_EQ(7, gJava_MyClassNatives_fooIOO_calls);
+
+  gJava_MyClassNatives_fooIOO_calls = 0;
 }
 
+JNI_TEST(CompileAndRunIntObjectObjectMethod)
+
 int gJava_MyClassNatives_fooSII_calls = 0;
 jint Java_MyClassNatives_fooSII(JNIEnv* env, jclass klass, jint x, jint y) {
   // 1 = klass
@@ -390,7 +478,7 @@
   return x + y;
 }
 
-TEST_F(JniCompilerTest, CompileAndRunStaticIntIntMethod) {
+void JniCompilerTest::CompileAndRunStaticIntIntMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSII", "(II)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSII));
@@ -399,8 +487,12 @@
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 20, 30);
   EXPECT_EQ(50, result);
   EXPECT_EQ(1, gJava_MyClassNatives_fooSII_calls);
+
+  gJava_MyClassNatives_fooSII_calls = 0;
 }
 
+JNI_TEST(CompileAndRunStaticIntIntMethod)
+
 int gJava_MyClassNatives_fooSDD_calls = 0;
 jdouble Java_MyClassNatives_fooSDD(JNIEnv* env, jclass klass, jdouble x, jdouble y) {
   // 1 = klass
@@ -414,7 +506,7 @@
   return x - y;  // non-commutative operator
 }
 
-TEST_F(JniCompilerTest, CompileAndRunStaticDoubleDoubleMethod) {
+void JniCompilerTest::CompileAndRunStaticDoubleDoubleMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSDD", "(DD)D",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSDD));
@@ -428,8 +520,12 @@
   result = env_->CallStaticDoubleMethod(jklass_, jmethod_, a, b);
   EXPECT_EQ(a - b, result);
   EXPECT_EQ(2, gJava_MyClassNatives_fooSDD_calls);
+
+  gJava_MyClassNatives_fooSDD_calls = 0;
 }
 
+JNI_TEST(CompileAndRunStaticDoubleDoubleMethod)
+
 // The x86 generic JNI code had a bug where it assumed a floating
 // point return value would be in xmm0. We use log, to somehow ensure
 // the compiler will use the floating point stack.
@@ -438,30 +534,30 @@
   return log(x);
 }
 
-TEST_F(JniCompilerTest, RunGenericStaticLogDoubleethod) {
+void JniCompilerTest::RunStaticLogDoubleMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
-  TEST_DISABLED_FOR_MIPS();
-  SetUpForTest(true, "logD", "(D)D",
-               reinterpret_cast<void*>(&Java_MyClassNatives_logD), true);
+  SetUpForTest(true, "logD", "(D)D", reinterpret_cast<void*>(&Java_MyClassNatives_logD));
 
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
   EXPECT_EQ(log(2.0), result);
 }
 
+JNI_TEST(RunStaticLogDoubleMethod)
+
 jfloat Java_MyClassNatives_logF(JNIEnv* env, jclass klass, jfloat x) {
   return logf(x);
 }
 
-TEST_F(JniCompilerTest, RunGenericStaticLogFloatMethod) {
+void JniCompilerTest::RunStaticLogFloatMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
-  TEST_DISABLED_FOR_MIPS();
-  SetUpForTest(true, "logF", "(F)F",
-               reinterpret_cast<void*>(&Java_MyClassNatives_logF), true);
+  SetUpForTest(true, "logF", "(F)F", reinterpret_cast<void*>(&Java_MyClassNatives_logF));
 
   jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
   EXPECT_EQ(logf(2.0), result);
 }
 
+JNI_TEST(RunStaticLogFloatMethod)
+
 jboolean Java_MyClassNatives_returnTrue(JNIEnv* env, jclass klass) {
   return JNI_TRUE;
 }
@@ -474,36 +570,37 @@
   return 42;
 }
 
-TEST_F(JniCompilerTest, RunGenericStaticReturnTrue) {
+void JniCompilerTest::RunStaticReturnTrueImpl() {
   TEST_DISABLED_FOR_PORTABLE();
-  TEST_DISABLED_FOR_MIPS();
-  SetUpForTest(true, "returnTrue", "()Z",
-               reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue), true);
+  SetUpForTest(true, "returnTrue", "()Z", reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue));
 
   jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
   EXPECT_TRUE(result);
 }
 
-TEST_F(JniCompilerTest, RunGenericStaticReturnFalse) {
+JNI_TEST(RunStaticReturnTrue)
+
+void JniCompilerTest::RunStaticReturnFalseImpl() {
   TEST_DISABLED_FOR_PORTABLE();
-  TEST_DISABLED_FOR_MIPS();
   SetUpForTest(true, "returnFalse", "()Z",
-               reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse), true);
+               reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse));
 
   jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
   EXPECT_FALSE(result);
 }
 
-TEST_F(JniCompilerTest, RunGenericStaticReturnInt) {
+JNI_TEST(RunStaticReturnFalse)
+
+void JniCompilerTest::RunGenericStaticReturnIntImpl() {
   TEST_DISABLED_FOR_PORTABLE();
-  TEST_DISABLED_FOR_MIPS();
-  SetUpForTest(true, "returnInt", "()I",
-               reinterpret_cast<void*>(&Java_MyClassNatives_returnInt), true);
+  SetUpForTest(true, "returnInt", "()I", reinterpret_cast<void*>(&Java_MyClassNatives_returnInt));
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_);
   EXPECT_EQ(42, result);
 }
 
+JNI_TEST(RunGenericStaticReturnInt)
+
 int gJava_MyClassNatives_fooSIOO_calls = 0;
 jobject Java_MyClassNatives_fooSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
                              jobject z) {
@@ -528,7 +625,7 @@
 }
 
 
-TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
+void JniCompilerTest::CompileAndRunStaticIntObjectObjectMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
@@ -558,8 +655,12 @@
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
   EXPECT_EQ(7, gJava_MyClassNatives_fooSIOO_calls);
+
+  gJava_MyClassNatives_fooSIOO_calls = 0;
 }
 
+JNI_TEST(CompileAndRunStaticIntObjectObjectMethod)
+
 int gJava_MyClassNatives_fooSSIOO_calls = 0;
 jobject Java_MyClassNatives_fooSSIOO(JNIEnv* env, jclass klass, jint x, jobject y, jobject z) {
   // 3 = klass + y + z
@@ -582,7 +683,7 @@
   }
 }
 
-TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
+void JniCompilerTest::CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
@@ -612,14 +713,18 @@
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
   EXPECT_EQ(7, gJava_MyClassNatives_fooSSIOO_calls);
+
+  gJava_MyClassNatives_fooSSIOO_calls = 0;
 }
 
+JNI_TEST(CompileAndRunStaticSynchronizedIntObjectObjectMethod)
+
 void Java_MyClassNatives_throwException(JNIEnv* env, jobject) {
   jclass c = env->FindClass("java/lang/RuntimeException");
   env->ThrowNew(c, "hello");
 }
 
-TEST_F(JniCompilerTest, ExceptionHandling) {
+void JniCompilerTest::ExceptionHandlingImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   {
     ASSERT_FALSE(runtime_->IsStarted());
@@ -660,8 +765,12 @@
   SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(2, gJava_MyClassNatives_foo_calls);
+
+  gJava_MyClassNatives_foo_calls = 0;
 }
 
+JNI_TEST(ExceptionHandling)
+
 jint Java_MyClassNatives_nativeUpCall(JNIEnv* env, jobject thisObj, jint i) {
   if (i <= 0) {
     // We want to check raw Object* / Array* below
@@ -700,7 +809,7 @@
   }
 }
 
-TEST_F(JniCompilerTest, NativeStackTraceElement) {
+void JniCompilerTest::NativeStackTraceElementImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_nativeUpCall));
@@ -708,11 +817,13 @@
   EXPECT_EQ(10+9+8+7+6+5+4+3+2+1, result);
 }
 
+JNI_TEST(NativeStackTraceElement)
+
 jobject Java_MyClassNatives_fooO(JNIEnv* env, jobject, jobject x) {
   return env->NewGlobalRef(x);
 }
 
-TEST_F(JniCompilerTest, ReturnGlobalRef) {
+void JniCompilerTest::ReturnGlobalRefImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooO));
@@ -721,6 +832,8 @@
   EXPECT_TRUE(env_->IsSameObject(result, jobj_));
 }
 
+JNI_TEST(ReturnGlobalRef)
+
 jint local_ref_test(JNIEnv* env, jobject thisObj, jint x) {
   // Add 10 local references
   ScopedObjectAccess soa(env);
@@ -730,7 +843,7 @@
   return x+1;
 }
 
-TEST_F(JniCompilerTest, LocalReferenceTableClearingTest) {
+void JniCompilerTest::LocalReferenceTableClearingTestImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I", reinterpret_cast<void*>(&local_ref_test));
   // 1000 invocations of a method that adds 10 local references
@@ -740,6 +853,8 @@
   }
 }
 
+JNI_TEST(LocalReferenceTableClearingTest)
+
 void my_arraycopy(JNIEnv* env, jclass klass, jobject src, jint src_pos, jobject dst, jint dst_pos, jint length) {
   EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jklass_, klass));
   EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jklass_, dst));
@@ -749,13 +864,15 @@
   EXPECT_EQ(9876, length);
 }
 
-TEST_F(JniCompilerTest, JavaLangSystemArrayCopy) {
+void JniCompilerTest::JavaLangSystemArrayCopyImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V",
                reinterpret_cast<void*>(&my_arraycopy));
   env_->CallStaticVoidMethod(jklass_, jmethod_, jobj_, 1234, jklass_, 5678, 9876);
 }
 
+JNI_TEST(JavaLangSystemArrayCopy)
+
 jboolean my_casi(JNIEnv* env, jobject unsafe, jobject obj, jlong offset, jint expected, jint newval) {
   EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jobj_, unsafe));
   EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jobj_, obj));
@@ -765,7 +882,7 @@
   return JNI_TRUE;
 }
 
-TEST_F(JniCompilerTest, CompareAndSwapInt) {
+void JniCompilerTest::CompareAndSwapIntImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
                reinterpret_cast<void*>(&my_casi));
@@ -774,6 +891,8 @@
   EXPECT_EQ(result, JNI_TRUE);
 }
 
+JNI_TEST(CompareAndSwapInt)
+
 jint my_gettext(JNIEnv* env, jclass klass, jlong val1, jobject obj1, jlong val2, jobject obj2) {
   EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   EXPECT_TRUE(env->IsSameObject(JniCompilerTest::jobj_, obj1));
@@ -783,7 +902,7 @@
   return 42;
 }
 
-TEST_F(JniCompilerTest, GetText) {
+void JniCompilerTest::GetTextImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "getText", "(JLjava/lang/Object;JLjava/lang/Object;)I",
                reinterpret_cast<void*>(&my_gettext));
@@ -792,6 +911,8 @@
   EXPECT_EQ(result, 42);
 }
 
+JNI_TEST(GetText)
+
 int gJava_MyClassNatives_GetSinkProperties_calls = 0;
 jarray Java_MyClassNatives_GetSinkProperties(JNIEnv* env, jobject thisObj, jstring s) {
   // 1 = thisObj
@@ -809,7 +930,7 @@
   return nullptr;
 }
 
-TEST_F(JniCompilerTest, GetSinkPropertiesNative) {
+void JniCompilerTest::GetSinkPropertiesNativeImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_GetSinkProperties));
@@ -819,8 +940,12 @@
       env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, nullptr));
   EXPECT_EQ(nullptr, result);
   EXPECT_EQ(1, gJava_MyClassNatives_GetSinkProperties_calls);
+
+  gJava_MyClassNatives_GetSinkProperties_calls = 0;
 }
 
+JNI_TEST(GetSinkPropertiesNative)
+
 // This should return jclass, but we're imitating a bug pattern.
 jobject Java_MyClassNatives_instanceMethodThatShouldReturnClass(JNIEnv* env, jobject) {
   return env->NewStringUTF("not a class!");
@@ -831,7 +956,7 @@
   return env->NewStringUTF("not a class!");
 }
 
-TEST_F(JniCompilerTest, UpcallReturnTypeChecking_Instance) {
+void JniCompilerTest::UpcallReturnTypeChecking_InstanceImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "instanceMethodThatShouldReturnClass", "()Ljava/lang/Class;",
                reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldReturnClass));
@@ -849,7 +974,9 @@
   check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticObjectMethodV");
 }
 
-TEST_F(JniCompilerTest, UpcallReturnTypeChecking_Static) {
+JNI_TEST(UpcallReturnTypeChecking_Instance)
+
+void JniCompilerTest::UpcallReturnTypeChecking_StaticImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "staticMethodThatShouldReturnClass", "()Ljava/lang/Class;",
                reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldReturnClass));
@@ -867,6 +994,8 @@
   check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallObjectMethodV");
 }
 
+JNI_TEST(UpcallReturnTypeChecking_Static)
+
 // This should take jclass, but we're imitating a bug pattern.
 void Java_MyClassNatives_instanceMethodThatShouldTakeClass(JNIEnv*, jobject, jclass) {
 }
@@ -875,7 +1004,7 @@
 void Java_MyClassNatives_staticMethodThatShouldTakeClass(JNIEnv*, jclass, jclass) {
 }
 
-TEST_F(JniCompilerTest, UpcallArgumentTypeChecking_Instance) {
+void JniCompilerTest::UpcallArgumentTypeChecking_InstanceImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "instanceMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldTakeClass));
@@ -886,7 +1015,9 @@
   check_jni_abort_catcher.Check("bad arguments passed to void MyClassNatives.instanceMethodThatShouldTakeClass(int, java.lang.Class)");
 }
 
-TEST_F(JniCompilerTest, UpcallArgumentTypeChecking_Static) {
+JNI_TEST(UpcallArgumentTypeChecking_Instance)
+
+void JniCompilerTest::UpcallArgumentTypeChecking_StaticImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "staticMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldTakeClass));
@@ -897,6 +1028,8 @@
   check_jni_abort_catcher.Check("bad arguments passed to void MyClassNatives.staticMethodThatShouldTakeClass(int, java.lang.Class)");
 }
 
+JNI_TEST(UpcallArgumentTypeChecking_Static)
+
 jfloat Java_MyClassNatives_checkFloats(JNIEnv* env, jobject thisObj, jfloat f1, jfloat f2) {
   EXPECT_EQ(kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
@@ -907,7 +1040,7 @@
   return f1 - f2;  // non-commutative operator
 }
 
-TEST_F(JniCompilerTest, CompileAndRunFloatFloatMethod) {
+void JniCompilerTest::CompileAndRunFloatFloatMethodImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "checkFloats", "(FF)F",
                reinterpret_cast<void*>(&Java_MyClassNatives_checkFloats));
@@ -921,6 +1054,8 @@
   EXPECT_EQ(a - b, result);
 }
 
+JNI_TEST(CompileAndRunFloatFloatMethod)
+
 void Java_MyClassNatives_checkParameterAlign(JNIEnv* env, jobject thisObj, jint i1, jlong l1) {
 //  EXPECT_EQ(kNative, Thread::Current()->GetState());
 //  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
@@ -932,7 +1067,7 @@
   EXPECT_EQ(l1, INT64_C(0x12345678ABCDEF0));
 }
 
-TEST_F(JniCompilerTest, CheckParameterAlign) {
+void JniCompilerTest::CheckParameterAlignImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "checkParameterAlign", "(IJ)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_checkParameterAlign));
@@ -940,6 +1075,8 @@
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_, 1234, INT64_C(0x12345678ABCDEF0));
 }
 
+JNI_TEST(CheckParameterAlign)
+
 void Java_MyClassNatives_maxParamNumber(JNIEnv* env, jobject thisObj,
     jobject o0, jobject o1, jobject o2, jobject o3, jobject o4, jobject o5, jobject o6, jobject o7,
     jobject o8, jobject o9, jobject o10, jobject o11, jobject o12, jobject o13, jobject o14, jobject o15,
@@ -1345,7 +1482,7 @@
     "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;"
     "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V";
 
-TEST_F(JniCompilerTest, MaxParamNumber) {
+void JniCompilerTest::MaxParamNumberImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "maxParamNumber", longSig,
                reinterpret_cast<void*>(&Java_MyClassNatives_maxParamNumber));
@@ -1369,7 +1506,9 @@
   env_->CallNonvirtualVoidMethodA(jobj_, jklass_, jmethod_, args);
 }
 
-TEST_F(JniCompilerTest, WithoutImplementation) {
+JNI_TEST(MaxParamNumber)
+
+void JniCompilerTest::WithoutImplementationImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "withoutImplementation", "()V", nullptr);
 
@@ -1379,6 +1518,8 @@
   EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
 }
 
+JNI_TEST(WithoutImplementation)
+
 void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3,
                                             jint i4, jint i5, jint i6, jint i7, jint i8, jint i9,
                                             jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4,
@@ -1417,7 +1558,7 @@
   EXPECT_EQ(i20, 20);
 }
 
-TEST_F(JniCompilerTest, StackArgsIntsFirst) {
+void JniCompilerTest::StackArgsIntsFirstImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst));
@@ -1448,6 +1589,8 @@
                              f3, f4, f5, f6, f7, f8, f9, f10);
 }
 
+JNI_TEST(StackArgsIntsFirst)
+
 void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv* env, jclass klass, jfloat f1, jfloat f2,
                                               jfloat f3, jfloat f4, jfloat f5, jfloat f6, jfloat f7,
                                               jfloat f8, jfloat f9, jfloat f10, jint i1, jint i2,
@@ -1486,7 +1629,7 @@
   EXPECT_EQ(i20, 20);
 }
 
-TEST_F(JniCompilerTest, StackArgsFloatsFirst) {
+void JniCompilerTest::StackArgsFloatsFirstImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst));
@@ -1517,6 +1660,8 @@
                              i4, i5, i6, i7, i8, i9, i10);
 }
 
+JNI_TEST(StackArgsFloatsFirst)
+
 void Java_MyClassNatives_stackArgsMixed(JNIEnv* env, jclass klass, jint i1, jfloat f1, jint i2,
                                         jfloat f2, jint i3, jfloat f3, jint i4, jfloat f4, jint i5,
                                         jfloat f5, jint i6, jfloat f6, jint i7, jfloat f7, jint i8,
@@ -1554,7 +1699,7 @@
   EXPECT_EQ(i20, 20);
 }
 
-TEST_F(JniCompilerTest, StackArgsMixed) {
+void JniCompilerTest::StackArgsMixedImpl() {
   TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed));
@@ -1585,4 +1730,6 @@
                              f7, i8, f8, i9, f9, i10, f10);
 }
 
+JNI_TEST(StackArgsMixed)
+
 }  // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 0fb1575..41a91ed 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,6 +18,7 @@
 
 #include <zlib.h>
 
+#include "base/allocator.h"
 #include "base/bit_vector.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 24e0277..12337c9 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -143,7 +143,7 @@
  protected:
   CodeGenerator(HGraph* graph, size_t number_of_registers)
       : frame_size_(kUninitializedFrameSize),
-        core_spill_mask_(-1),
+        core_spill_mask_(0),
         graph_(graph),
         block_labels_(graph->GetArena(), 0),
         pc_infos_(graph->GetArena(), 32),
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index bd3a7d9..da13b1e 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -16,6 +16,7 @@
 
 #include "register_allocator.h"
 
+#include "base/bit_vector-inl.h"
 #include "code_generator.h"
 #include "ssa_liveness_analysis.h"
 
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index fbdc0b9..5de1ab9 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -16,6 +16,7 @@
 
 #include "ssa_liveness_analysis.h"
 
+#include "base/bit_vector-inl.h"
 #include "code_generator.h"
 #include "nodes.h"
 
diff --git a/compiler/utils/arena_bit_vector.cc b/compiler/utils/arena_bit_vector.cc
index 39f7d18..de35f3d 100644
--- a/compiler/utils/arena_bit_vector.cc
+++ b/compiler/utils/arena_bit_vector.cc
@@ -16,11 +16,12 @@
 
 #include "arena_allocator.h"
 #include "arena_bit_vector.h"
+#include "base/allocator.h"
 
 namespace art {
 
 template <typename ArenaAlloc>
-class ArenaBitVectorAllocator : public Allocator {
+class ArenaBitVectorAllocator FINAL : public Allocator {
  public:
   explicit ArenaBitVectorAllocator(ArenaAlloc* arena) : arena_(arena) {}
   ~ArenaBitVectorAllocator() {}
@@ -37,7 +38,7 @@
   static void operator delete(void* p) {}  // Nop.
 
  private:
-  ArenaAlloc* arena_;
+  ArenaAlloc* const arena_;
   DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator);
 };
 
diff --git a/compiler/utils/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h
index 485ed76..c92658f 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -51,23 +51,23 @@
  * A BitVector implementation that uses Arena allocation.
  */
 class ArenaBitVector : public BitVector {
-  public:
-    ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
-                   OatBitMapKind kind = kBitMapMisc);
-    ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
-                   OatBitMapKind kind = kBitMapMisc);
-    ~ArenaBitVector() {}
+ public:
+  ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
+                 OatBitMapKind kind = kBitMapMisc);
+  ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
+                 OatBitMapKind kind = kBitMapMisc);
+  ~ArenaBitVector() {}
 
   static void* operator new(size_t size, ArenaAllocator* arena) {
-     return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+    return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
   }
   static void* operator new(size_t size, ScopedArenaAllocator* arena) {
-     return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+    return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
   }
   static void operator delete(void* p) {}  // Nop.
 
-  private:
-    const OatBitMapKind kind_;      // for memory use tuning. TODO: currently unused.
+ private:
+  const OatBitMapKind kind_;      // for memory use tuning. TODO: currently unused.
 };
 
 
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 72ad9a5..bbdf3a3 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -823,22 +823,12 @@
     if (log_options) {
       LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i];
     }
-    // TODO: GetInstructionSetFromString shouldn't LOG(FATAL).
     if (option.starts_with("--instruction-set=")) {
       isa_set = true;
       const char* isa_str = option.substr(strlen("--instruction-set=")).data();
-      if (!strcmp("arm", isa_str)) {
-        isa = kArm;
-      } else if (!strcmp("arm64", isa_str)) {
-        isa = kArm64;
-      } else if (!strcmp("x86", isa_str)) {
-        isa = kX86;
-      } else if (!strcmp("x86_64", isa_str)) {
-        isa = kX86_64;
-      } else if (!strcmp("mips", isa_str)) {
-        isa = kMips;
-      } else {
-        Usage("Unknown instruction set %s", isa_str);
+      isa = GetInstructionSetFromString(isa_str);
+      if (isa == kNone) {
+        Usage("Unknown or invalid instruction set %s", isa_str);
       }
     } else if (option.starts_with("--input-oat-location=")) {
       if (have_input_oat) {
diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S
index 925e428..babdbf5 100644
--- a/runtime/arch/arm/arm_sdiv.S
+++ b/runtime/arch/arm/arm_sdiv.S
@@ -9,7 +9,7 @@
 #include "asm_support_arm.S"
 
 .section .text
-ENTRY CheckForARMSDIVInstruction
+ENTRY_NO_HIDE CheckForARMSDIVInstruction
   mov r1,#1
   // depending on the architecture, the assembler will not allow an
   // sdiv instruction, so we will have to output the bytes directly.
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index e1b0ce7..a3e3b21 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -33,6 +33,7 @@
 .macro ENTRY name
     .thumb_func
     .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
     .global \name
     /* Cache alignment for function entry */
     .balign 16
@@ -41,9 +42,35 @@
     .fnstart
 .endm
 
+.macro ENTRY_NO_HIDE name
+    .thumb_func
+    .type \name, #function
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+    .cfi_startproc
+    .fnstart
+.endm
+
+
 .macro ARM_ENTRY name
     .arm
     .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+    .cfi_startproc
+     /* Ensure we get a sane starting CFA. */
+    .cfi_def_cfa sp,0
+    .fnstart
+.endm
+
+.macro ARM_ENTRY_NO_HIDE name
+    .arm
+    .type \name, #function
     .global \name
     /* Cache alignment for function entry */
     .balign 16
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
index 98d17dc..3491c18 100644
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ b/runtime/arch/arm/portable_entrypoints_arm.S
@@ -138,7 +138,7 @@
 END art_portable_resolution_trampoline
 
     .extern artPortableToInterpreterBridge
-ENTRY art_portable_to_interpreter_bridge
+ENTRY_NO_HIDE art_portable_to_interpreter_bridge
     @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
     @ TODO: just save the registers that are needed in artPortableToInterpreterBridge.
     push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 9f0db8c..1b30c9c 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -308,9 +308,12 @@
 #ifdef ARM_R4_SUSPEND_FLAG
     mov    r4, #SUSPEND_CHECK_INTERVAL     @ reset r4 to suspend check interval
 #endif
-    add    r5, r2, #16                     @ create space for method pointer in frame
-    and    r5, #0xFFFFFFF0                 @ align frame size to 16 bytes
-    sub    sp, r5                          @ reserve stack space for argument array
+    add    r5, r2, #4                      @ create space for method pointer in frame
+
+    sub    r5, sp, r5                      @ reserve & align *stack* to 16 bytes: native calling
+    and    r5, #0xFFFFFFF0                 @ convention only aligns to 8B, so we have to ensure ART
+    mov    sp, r5                          @ 16B alignment ourselves.
+
     add    r0, sp, #4                      @ pass stack pointer + method ptr as dest for memcpy
     bl     memcpy                          @ memcpy (dest, src, bytes)
     ldr    r0, [r11]                       @ restore method*
@@ -988,7 +991,7 @@
     /*
      * Called to do a generic JNI down-call
      */
-ENTRY art_quick_generic_jni_trampoline
+ENTRY_NO_HIDE art_quick_generic_jni_trampoline
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     str r0, [sp, #0]  // Store native ArtMethod* to bottom of stack.
 
@@ -1083,7 +1086,7 @@
 END art_quick_generic_jni_trampoline
 
     .extern artQuickToInterpreterBridge
-ENTRY art_quick_to_interpreter_bridge
+ENTRY_NO_HIDE art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     mov     r1, r9                 @ pass Thread::Current
     mov     r2, sp                 @ pass SP
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index be167fa..fb49460 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -44,6 +44,16 @@
 
 .macro ENTRY name
     .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
+    .global \name
+    /* Cache alignment for function entry */
+    .balign 16
+\name:
+    .cfi_startproc
+.endm
+
+.macro ENTRY_NO_HIDE name
+    .type \name, #function
     .global \name
     /* Cache alignment for function entry */
     .balign 16
@@ -62,4 +72,10 @@
     END \name
 .endm
 
+.macro UNIMPLEMENTED_NO_HIDE name
+    ENTRY_NO_HIDE \name
+    brk 0
+    END \name
+.endm
+
 #endif  // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/arm64/portable_entrypoints_arm64.S
index e136885..41711b5 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/runtime/arch/arm64/portable_entrypoints_arm64.S
@@ -25,4 +25,4 @@
 
 UNIMPLEMENTED art_portable_resolution_trampoline
 
-UNIMPLEMENTED art_portable_to_interpreter_bridge
+UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index ab9035a..2a19e27 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1435,7 +1435,7 @@
     /*
      * Called to do a generic JNI down-call
      */
-ENTRY art_quick_generic_jni_trampoline
+ENTRY_NO_HIDE art_quick_generic_jni_trampoline
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
     str x0, [sp, #0]  // Store native ArtMethod* to bottom of stack.
 
@@ -1531,7 +1531,7 @@
  * x0 = method being called/to bridge to.
  * x1..x7, d0..d7 = arguments to that method.
  */
-ENTRY art_quick_to_interpreter_bridge
+ENTRY_NO_HIDE art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
 
     //  x0 will contain mirror::ArtMethod* method.
diff --git a/runtime/arch/memcmp16.cc b/runtime/arch/memcmp16.cc
index 7928085..5a3e73e 100644
--- a/runtime/arch/memcmp16.cc
+++ b/runtime/arch/memcmp16.cc
@@ -28,4 +28,16 @@
   return 0;
 }
 
+namespace art {
+
+namespace testing {
+
+int32_t MemCmp16Testing(const uint16_t* s0, const uint16_t* s1, size_t count) {
+  return MemCmp16(s0, s1, count);
+}
+
+}
+
+}  // namespace art
+
 #pragma GCC diagnostic warning "-Wunused-function"
diff --git a/runtime/arch/memcmp16.h b/runtime/arch/memcmp16.h
index 14dc1e3..4b9fb8e 100644
--- a/runtime/arch/memcmp16.h
+++ b/runtime/arch/memcmp16.h
@@ -50,4 +50,17 @@
 extern "C" int32_t memcmp16_generic_static(const uint16_t* s0, const uint16_t* s1, size_t count);
 #endif
 
+namespace art {
+
+namespace testing {
+
+// A version that is exposed and relatively "close to the metal," so that memcmp16_test can do
+// some reasonable testing. Without this, as __memcmp16 is hidden, the test cannot access the
+// implementation.
+int32_t MemCmp16Testing(const uint16_t* s0, const uint16_t* s1, size_t count);
+
+}
+
+}  // namespace art
+
 #endif  // ART_RUNTIME_ARCH_MEMCMP16_H_
diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc
index 5747c67..5ba06f8 100644
--- a/runtime/arch/memcmp16_test.cc
+++ b/runtime/arch/memcmp16_test.cc
@@ -139,7 +139,7 @@
     size_t mod_min = c1_mod < c2_mod ? c1_mod : c2_mod;
 
     int32_t expected = memcmp16_compare(s1_pot_unaligned, s2_pot_unaligned, mod_min);
-    int32_t computed = MemCmp16(s1_pot_unaligned, s2_pot_unaligned, mod_min);
+    int32_t computed = art::testing::MemCmp16Testing(s1_pot_unaligned, s2_pot_unaligned, mod_min);
 
     ASSERT_EQ(expected, computed) << "Run " << round << ", c1=" << count1 << " c2=" << count2;
 
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 25f9a5a..864e3f7 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -17,6 +17,7 @@
 #include <cstdio>
 
 #include "common_runtime_test.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -543,15 +544,21 @@
 #endif
   }
 
+  static uintptr_t GetEntrypoint(Thread* self, QuickEntrypointEnum entrypoint) {
+    int32_t offset;
+#ifdef __LP64__
+    offset = GetThreadOffset<8>(entrypoint).Int32Value();
+#else
+    offset = GetThreadOffset<4>(entrypoint).Int32Value();
+#endif
+    return *reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(self) + offset);
+  }
+
  protected:
   size_t fp_result;
 };
 
 
-#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_memcpy(void);
-#endif
-
 TEST_F(StubTest, Memcpy) {
 #if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__))
   Thread* self = Thread::Current();
@@ -564,7 +571,7 @@
   }
 
   Invoke3(reinterpret_cast<size_t>(&trg[4]), reinterpret_cast<size_t>(&orig[4]),
-          10 * sizeof(uint32_t), reinterpret_cast<uintptr_t>(&art_quick_memcpy), self);
+          10 * sizeof(uint32_t), StubTest::GetEntrypoint(self, kQuickMemcpy), self);
 
   EXPECT_EQ(orig[0], trg[0]);
 
@@ -589,15 +596,14 @@
 #endif
 }
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_lock_object(void);
-#endif
-
 TEST_F(StubTest, LockObject) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   static constexpr size_t kThinLockLoops = 100;
 
   Thread* self = Thread::Current();
+
+  const uintptr_t art_quick_lock_object = StubTest::GetEntrypoint(self, kQuickLockObject);
+
   // Create an object
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
@@ -609,8 +615,7 @@
   LockWord::LockState old_state = lock.GetState();
   EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
 
-  Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+  Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_lock_object, self);
 
   LockWord lock_after = obj->GetLockWord(false);
   LockWord::LockState new_state = lock_after.GetState();
@@ -618,8 +623,7 @@
   EXPECT_EQ(lock_after.ThinLockCount(), 0U);  // Thin lock starts count at zero
 
   for (size_t i = 1; i < kThinLockLoops; ++i) {
-    Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U,
-              reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+    Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_lock_object, self);
 
     // Check we're at lock count i
 
@@ -635,8 +639,7 @@
 
   obj2->IdentityHashCode();
 
-  Invoke3(reinterpret_cast<size_t>(obj2.Get()), 0U, 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+  Invoke3(reinterpret_cast<size_t>(obj2.Get()), 0U, 0U, art_quick_lock_object, self);
 
   LockWord lock_after2 = obj2->GetLockWord(false);
   LockWord::LockState new_state2 = lock_after2.GetState();
@@ -665,17 +668,15 @@
 };
 
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_lock_object(void);
-extern "C" void art_quick_unlock_object(void);
-#endif
-
 // NO_THREAD_SAFETY_ANALYSIS as we do not want to grab exclusive mutator lock for MonitorInfo.
 static void TestUnlockObject(StubTest* test) NO_THREAD_SAFETY_ANALYSIS {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   static constexpr size_t kThinLockLoops = 100;
 
   Thread* self = Thread::Current();
+
+  const uintptr_t art_quick_lock_object = StubTest::GetEntrypoint(self, kQuickLockObject);
+  const uintptr_t art_quick_unlock_object = StubTest::GetEntrypoint(self, kQuickUnlockObject);
   // Create an object
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
@@ -687,8 +688,7 @@
   LockWord::LockState old_state = lock.GetState();
   EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
 
-  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U,
-                reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_unlock_object, self);
   // This should be an illegal monitor state.
   EXPECT_TRUE(self->IsExceptionPending());
   self->ClearException();
@@ -697,15 +697,13 @@
   LockWord::LockState new_state = lock_after.GetState();
   EXPECT_EQ(LockWord::LockState::kUnlocked, new_state);
 
-  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U,
-                reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_lock_object, self);
 
   LockWord lock_after2 = obj->GetLockWord(false);
   LockWord::LockState new_state2 = lock_after2.GetState();
   EXPECT_EQ(LockWord::LockState::kThinLocked, new_state2);
 
-  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U,
-                reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+  test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_unlock_object, self);
 
   LockWord lock_after3 = obj->GetLockWord(false);
   LockWord::LockState new_state3 = lock_after3.GetState();
@@ -759,12 +757,12 @@
       }
 
       if (lock) {
-        test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U,
-                       reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+        test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U, art_quick_lock_object,
+                      self);
         counts[index]++;
       } else {
         test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U,
-                      reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+                      art_quick_unlock_object, self);
         counts[index]--;
       }
 
@@ -795,8 +793,8 @@
     size_t index = kNumberOfLocks - 1 - i;
     size_t count = counts[index];
     while (count > 0) {
-      test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U,
-                    reinterpret_cast<uintptr_t>(&art_quick_unlock_object), self);
+      test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U, art_quick_unlock_object,
+                    self);
       count--;
     }
 
@@ -825,6 +823,9 @@
 TEST_F(StubTest, CheckCast) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   Thread* self = Thread::Current();
+
+  const uintptr_t art_quick_check_cast = StubTest::GetEntrypoint(self, kQuickCheckCast);
+
   // Find some classes.
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
@@ -838,24 +839,24 @@
   EXPECT_FALSE(self->IsExceptionPending());
 
   Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+          art_quick_check_cast, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
 
   Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+          art_quick_check_cast, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
 
   Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+          art_quick_check_cast, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
 
   // TODO: Make the following work. But that would require correct managed frames.
 
   Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
-          reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+          art_quick_check_cast, self);
 
   EXPECT_TRUE(self->IsExceptionPending());
   self->ClearException();
@@ -868,16 +869,16 @@
 }
 
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void);
-// Do not check non-checked ones, we'd need handlers and stuff...
-#endif
-
 TEST_F(StubTest, APutObj) {
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
 
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   Thread* self = Thread::Current();
+
+  // Do not check non-checked ones, we'd need handlers and stuff...
+  const uintptr_t art_quick_aput_obj_with_null_and_bound_check =
+      StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck);
+
   // Create an object
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
@@ -907,25 +908,25 @@
   EXPECT_FALSE(self->IsExceptionPending());
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(str_obj.Get(), array->Get(0));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(str_obj.Get(), array->Get(1));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(str_obj.Get(), array->Get(2));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(str_obj.Get(), array->Get(3));
@@ -933,25 +934,25 @@
   // 1.2) Assign null to array[0..3]
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(nullptr, array->Get(0));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(nullptr, array->Get(1));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(nullptr, array->Get(2));
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(nullptr, array->Get(3));
@@ -972,7 +973,7 @@
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1),
           reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_TRUE(self->IsExceptionPending());
   self->ClearException();
@@ -980,7 +981,7 @@
   // 2.3) Index > 0
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_TRUE(self->IsExceptionPending());
   self->ClearException();
@@ -988,7 +989,7 @@
   // 3) Failure cases (obj into str[])
 
   Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()),
-          reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+          art_quick_aput_obj_with_null_and_bound_check, self);
 
   EXPECT_TRUE(self->IsExceptionPending());
   self->ClearException();
@@ -1024,7 +1025,7 @@
     size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
                             reinterpret_cast<size_t>(c->GetVirtualMethod(0)),  // arbitrary
                             0U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocObject),
+                            StubTest::GetEntrypoint(self, kQuickAllocObject),
                             self);
 
     EXPECT_FALSE(self->IsExceptionPending());
@@ -1038,7 +1039,7 @@
     // We can use nullptr in the second argument as we do not need a method here (not used in
     // resolved/initialized cases)
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 0U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocObjectResolved),
+                            StubTest::GetEntrypoint(self, kQuickAllocObjectResolved),
                             self);
 
     EXPECT_FALSE(self->IsExceptionPending());
@@ -1052,7 +1053,7 @@
     // We can use nullptr in the second argument as we do not need a method here (not used in
     // resolved/initialized cases)
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 0U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocObjectInitialized),
+                            StubTest::GetEntrypoint(self, kQuickAllocObjectInitialized),
                             self);
 
     EXPECT_FALSE(self->IsExceptionPending());
@@ -1108,7 +1109,7 @@
     self->ClearException();
 
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 0U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocObjectInitialized),
+                            StubTest::GetEntrypoint(self, kQuickAllocObjectInitialized),
                             self);
     EXPECT_TRUE(self->IsExceptionPending());
     self->ClearException();
@@ -1154,7 +1155,7 @@
     size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
                             reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)),  // arbitrary
                             10U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocArray),
+                            StubTest::GetEntrypoint(self, kQuickAllocArray),
                             self);
 
     EXPECT_FALSE(self->IsExceptionPending());
@@ -1169,7 +1170,7 @@
     // We can use nullptr in the second argument as we do not need a method here (not used in
     // resolved/initialized cases)
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 10U,
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocArrayResolved),
+                            StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
                             self);
     EXPECT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
     EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
@@ -1188,7 +1189,7 @@
   {
     size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr),
                             GB,  // that should fail...
-                            reinterpret_cast<uintptr_t>(GetTlsPtr(self)->quick_entrypoints.pAllocArrayResolved),
+                            StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
                             self);
 
     EXPECT_TRUE(self->IsExceptionPending());
@@ -1205,10 +1206,6 @@
 }
 
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_string_compareto(void);
-#endif
-
 TEST_F(StubTest, StringCompareTo) {
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
 
@@ -1216,6 +1213,9 @@
   // TODO: Check the "Unresolved" allocation stubs
 
   Thread* self = Thread::Current();
+
+  const uintptr_t art_quick_string_compareto = StubTest::GetEntrypoint(self, kQuickStringCompareTo);
+
   ScopedObjectAccess soa(self);
   // garbage is created during ClassLinker::Init
 
@@ -1274,7 +1274,7 @@
       // Test string_compareto x y
       size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()),
                               reinterpret_cast<size_t>(s[y].Get()), 0U,
-                              reinterpret_cast<uintptr_t>(&art_quick_string_compareto), self);
+                              art_quick_string_compareto, self);
 
       EXPECT_FALSE(self->IsExceptionPending());
 
@@ -1306,11 +1306,6 @@
 }
 
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_set32_static(void);
-extern "C" void art_quick_get32_static(void);
-#endif
-
 static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
                            mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1322,13 +1317,13 @@
     test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                               static_cast<size_t>(values[i]),
                               0U,
-                              reinterpret_cast<uintptr_t>(&art_quick_set32_static),
+                              StubTest::GetEntrypoint(self, kQuickSet32Static),
                               self,
                               referrer);
 
     size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                            0U, 0U,
-                                           reinterpret_cast<uintptr_t>(&art_quick_get32_static),
+                                           StubTest::GetEntrypoint(self, kQuickGet32Static),
                                            self,
                                            referrer);
 
@@ -1342,11 +1337,6 @@
 }
 
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_set32_instance(void);
-extern "C" void art_quick_get32_instance(void);
-#endif
-
 static void GetSet32Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
                              Thread* self, mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1358,7 +1348,7 @@
     test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                               reinterpret_cast<size_t>(obj->Get()),
                               static_cast<size_t>(values[i]),
-                              reinterpret_cast<uintptr_t>(&art_quick_set32_instance),
+                              StubTest::GetEntrypoint(self, kQuickSet32Instance),
                               self,
                               referrer);
 
@@ -1371,7 +1361,7 @@
     size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                             reinterpret_cast<size_t>(obj->Get()),
                                             0U,
-                                            reinterpret_cast<uintptr_t>(&art_quick_get32_instance),
+                                            StubTest::GetEntrypoint(self, kQuickGet32Instance),
                                             self,
                                             referrer);
     EXPECT_EQ(res, static_cast<int32_t>(res2));
@@ -1385,8 +1375,6 @@
 
 
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_set_obj_static(void);
-extern "C" void art_quick_get_obj_static(void);
 
 static void set_and_check_static(uint32_t f_idx, mirror::Object* val, Thread* self,
                                  mirror::ArtMethod* referrer, StubTest* test)
@@ -1394,13 +1382,13 @@
   test->Invoke3WithReferrer(static_cast<size_t>(f_idx),
                             reinterpret_cast<size_t>(val),
                             0U,
-                            reinterpret_cast<uintptr_t>(&art_quick_set_obj_static),
+                            StubTest::GetEntrypoint(self, kQuickSetObjStatic),
                             self,
                             referrer);
 
   size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f_idx),
                                          0U, 0U,
-                                         reinterpret_cast<uintptr_t>(&art_quick_get_obj_static),
+                                         StubTest::GetEntrypoint(self, kQuickGetObjStatic),
                                          self,
                                          referrer);
 
@@ -1428,9 +1416,6 @@
 
 
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_set_obj_instance(void);
-extern "C" void art_quick_get_obj_instance(void);
-
 static void set_and_check_instance(Handle<mirror::ArtField>* f, mirror::Object* trg,
                                    mirror::Object* val, Thread* self, mirror::ArtMethod* referrer,
                                    StubTest* test)
@@ -1438,14 +1423,14 @@
   test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                             reinterpret_cast<size_t>(trg),
                             reinterpret_cast<size_t>(val),
-                            reinterpret_cast<uintptr_t>(&art_quick_set_obj_instance),
+                            StubTest::GetEntrypoint(self, kQuickSetObjInstance),
                             self,
                             referrer);
 
   size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                          reinterpret_cast<size_t>(trg),
                                          0U,
-                                         reinterpret_cast<uintptr_t>(&art_quick_get_obj_instance),
+                                         StubTest::GetEntrypoint(self, kQuickGetObjInstance),
                                          self,
                                          referrer);
 
@@ -1476,11 +1461,6 @@
 
 // TODO: Complete these tests for 32b architectures.
 
-#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__)
-extern "C" void art_quick_set64_static(void);
-extern "C" void art_quick_get64_static(void);
-#endif
-
 static void GetSet64Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
                            mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1491,13 +1471,13 @@
   for (size_t i = 0; i < num_values; ++i) {
     test->Invoke3UWithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                values[i],
-                               reinterpret_cast<uintptr_t>(&art_quick_set64_static),
+                               StubTest::GetEntrypoint(self, kQuickSet64Static),
                                self,
                                referrer);
 
     size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                            0U, 0U,
-                                           reinterpret_cast<uintptr_t>(&art_quick_get64_static),
+                                           StubTest::GetEntrypoint(self, kQuickGet64Static),
                                            self,
                                            referrer);
 
@@ -1511,11 +1491,6 @@
 }
 
 
-#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__)
-extern "C" void art_quick_set64_instance(void);
-extern "C" void art_quick_get64_instance(void);
-#endif
-
 static void GetSet64Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
                              Thread* self, mirror::ArtMethod* referrer, StubTest* test)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1527,7 +1502,7 @@
     test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                               reinterpret_cast<size_t>(obj->Get()),
                               static_cast<size_t>(values[i]),
-                              reinterpret_cast<uintptr_t>(&art_quick_set64_instance),
+                              StubTest::GetEntrypoint(self, kQuickSet64Instance),
                               self,
                               referrer);
 
@@ -1540,7 +1515,7 @@
     size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
                                             reinterpret_cast<size_t>(obj->Get()),
                                             0U,
-                                            reinterpret_cast<uintptr_t>(&art_quick_get64_instance),
+                                            StubTest::GetEntrypoint(self, kQuickGet64Instance),
                                             self,
                                             referrer);
     EXPECT_EQ(res, static_cast<int64_t>(res2));
@@ -1683,9 +1658,6 @@
   TestFields(self, this, Primitive::Type::kPrimLong);
 }
 
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
-extern "C" void art_quick_imt_conflict_trampoline(void);
-#endif
 
 TEST_F(StubTest, IMT) {
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
@@ -1716,7 +1688,7 @@
   // Patch up ArrayList.contains.
   if (contains_amethod.Get()->GetEntryPointFromQuickCompiledCode() == nullptr) {
     contains_amethod.Get()->SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(
-        GetTlsPtr(self)->quick_entrypoints.pQuickToInterpreterBridge));
+        StubTest::GetEntrypoint(self, kQuickQuickToInterpreterBridge)));
   }
 
   // List
@@ -1765,7 +1737,7 @@
   size_t result =
       Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
                                    reinterpret_cast<size_t>(obj.Get()),
-                                   reinterpret_cast<uintptr_t>(&art_quick_imt_conflict_trampoline),
+                                   StubTest::GetEntrypoint(self, kQuickQuickImtConflictTrampoline),
                                    self, contains_amethod.Get(),
                                    static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()));
 
@@ -1782,7 +1754,7 @@
 
   result = Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
                                         reinterpret_cast<size_t>(obj.Get()),
-                                        reinterpret_cast<uintptr_t>(&art_quick_imt_conflict_trampoline),
+                                        StubTest::GetEntrypoint(self, kQuickQuickImtConflictTrampoline),
                                         self, contains_amethod.Get(),
                                         static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()));
 
@@ -1795,10 +1767,6 @@
 #endif
 }
 
-#if defined(__arm__) || defined(__aarch64__)
-extern "C" void art_quick_indexof(void);
-#endif
-
 TEST_F(StubTest, StringIndexOf) {
 #if defined(__arm__) || defined(__aarch64__)
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
@@ -1848,7 +1816,7 @@
 
         // Test string_compareto x y
         size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()), c_char[y], start,
-                                reinterpret_cast<uintptr_t>(&art_quick_indexof), self);
+                                StubTest::GetEntrypoint(self, kQuickIndexOf), self);
 
         EXPECT_FALSE(self->IsExceptionPending());
 
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index 96c2c05..a578023 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -112,6 +112,7 @@
     #define PLT_SYMBOL(name) _ ## name
 #endif
 
+// Directive to hide a function symbol.
 #if defined(__APPLE__)
     #define ASM_HIDDEN .private_extern
 #else
@@ -125,6 +126,17 @@
 
 MACRO1(DEFINE_FUNCTION, c_name)
     FUNCTION_TYPE(\c_name, 0)
+    ASM_HIDDEN VAR(c_name, 0)
+    .globl VAR(c_name, 0)
+    ALIGN_FUNCTION_ENTRY
+VAR(c_name, 0):
+    CFI_STARTPROC
+    // Ensure we get a sane starting CFA.
+    CFI_DEF_CFA(esp, 4)
+END_MACRO
+
+MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name)
+    FUNCTION_TYPE(\c_name, 0)
     .globl VAR(c_name, 0)
     ALIGN_FUNCTION_ENTRY
 VAR(c_name, 0):
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 5f270f8..9365795 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -115,7 +115,7 @@
   ret
 END_FUNCTION art_portable_resolution_trampoline
 
-DEFINE_FUNCTION art_portable_to_interpreter_bridge
+DEFINE_FUNCTION_NO_HIDE art_portable_to_interpreter_bridge
   PUSH ebp                        // Set up frame.
   movl %esp, %ebp
   CFI_DEF_CFA_REGISTER(%ebp)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 084846a..2f3e317 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -106,7 +106,7 @@
     pushl %fs:THREAD_SELF_OFFSET             // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     SETUP_GOT_NOSAVE                         // clobbers ebx (harmless here)
-    call PLT_SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
+    call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
     int3                                     // unreached
 END_MACRO
 
@@ -121,7 +121,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
-    call PLT_VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
@@ -137,7 +137,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass arg1
     SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
@@ -153,7 +153,7 @@
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
     SETUP_GOT_NOSAVE              // clobbers ebx (harmless here)
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
     int3                          // unreached
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
@@ -161,7 +161,6 @@
     /*
      * Called by managed code to create and deliver a NullPointerException.
      */
-    ASM_HIDDEN art_quick_throw_null_pointer_exception
 NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
 
     /*
@@ -189,7 +188,6 @@
      * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds
      * index, arg2 holds limit.
      */
-    ASM_HIDDEN art_quick_throw_array_bounds
 TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
 
     /*
@@ -231,7 +229,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
     movl %edx, %edi               // save code pointer in EDI
     addl MACRO_LITERAL(36), %esp  // Pop arguments skip eax
     CFI_ADJUST_CFA_OFFSET(-36)
@@ -253,7 +251,6 @@
     END_FUNCTION RAW_VAR(c_name, 0)
 END_MACRO
 
-    ASM_HIDDEN art_quick_invoke_interface_trampoline
 INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline, artInvokeInterfaceTrampoline
 INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline_with_access_check, artInvokeInterfaceTrampolineWithAccessCheck
 
@@ -328,7 +325,7 @@
     PUSH edx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    call PLT_VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -347,7 +344,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass arg1
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -366,7 +363,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, Thread*, SP)
     addl MACRO_LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -388,7 +385,7 @@
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, arg2, arg3, Thread*, SP)
     addl MACRO_LITERAL(32), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -569,7 +566,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass object
-    call PLT_SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
     addl LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -603,7 +600,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass object
-    call PLT_SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
     addl LITERAL(16), %esp  // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -615,7 +612,7 @@
     PUSH eax                     // alignment padding
     PUSH ecx                     // pass arg2 - obj->klass
     PUSH eax                     // pass arg1 - checked class
-    call PLT_SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
+    call SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
     ret
@@ -626,7 +623,7 @@
     PUSH eax                     // alignment padding
     PUSH ecx                     // pass arg2 - obj->klass
     PUSH eax                     // pass arg1 - checked class
-    call PLT_SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
+    call SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
     testl %eax, %eax
     jz 1f                         // jump forward if not assignable
     addl LITERAL(12), %esp        // pop arguments
@@ -645,7 +642,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
-    call PLT_SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
     int3                          // unreached
 END_FUNCTION art_quick_check_cast
 
@@ -660,7 +657,6 @@
     jmp SYMBOL(art_quick_throw_null_pointer_exception)
 END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
 
-    ASM_HIDDEN art_quick_aput_obj_with_bound_check
 DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
     movl ARRAY_LENGTH_OFFSET(%eax), %ebx
     cmpl %ebx, %ecx
@@ -670,7 +666,6 @@
     jmp SYMBOL(art_quick_throw_array_bounds)
 END_FUNCTION art_quick_aput_obj_with_bound_check
 
-    ASM_HIDDEN art_quick_aput_obj
 DEFINE_FUNCTION art_quick_aput_obj
     test %edx, %edx              // store of null
     jz .Ldo_aput_null
@@ -697,7 +692,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ebx                     // pass arg1 - component type of the array
     SETUP_GOT_NOSAVE             // clobbers EBX
-    call PLT_SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
+    call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
     addl LITERAL(16), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     testl %eax, %eax
@@ -722,7 +717,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH edx                      // pass arg2 - value
     PUSH eax                      // pass arg1 - array
-    call PLT_SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
     int3                          // unreached
 END_FUNCTION art_quick_aput_obj
 
@@ -744,7 +739,7 @@
     PUSH ecx                      // pass arg2 a.hi
     PUSH eax                      // pass arg1 a.lo
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(art_d2l)      // (jdouble a)
+    call SYMBOL(art_d2l)      // (jdouble a)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
     ret
@@ -755,7 +750,7 @@
     CFI_ADJUST_CFA_OFFSET(8)
     SETUP_GOT_NOSAVE              // clobbers EBX
     PUSH eax                      // pass arg1 a
-    call PLT_SYMBOL(art_f2l)      // (jfloat a)
+    call SYMBOL(art_f2l)      // (jfloat a)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
     ret
@@ -769,7 +764,7 @@
     PUSH ecx                     // pass arg2 a.hi
     PUSH eax                     // pass arg1 a.lo
     SETUP_GOT_NOSAVE             // clobbers EBX
-    call PLT_SYMBOL(artLdiv)     // (jlong a, jlong b)
+    call SYMBOL(artLdiv)     // (jlong a, jlong b)
     addl LITERAL(28), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-28)
     ret
@@ -783,7 +778,7 @@
     PUSH ecx                     // pass arg2 a.hi
     PUSH eax                     // pass arg1 a.lo
     SETUP_GOT_NOSAVE             // clobbers EBX
-    call PLT_SYMBOL(artLmod)     // (jlong a, jlong b)
+    call SYMBOL(artLmod)     // (jlong a, jlong b)
     addl LITERAL(28), %esp       // pop arguments
     CFI_ADJUST_CFA_OFFSET(-28)
     ret
@@ -851,7 +846,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSet32InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*, SP)
+    call SYMBOL(artSet32InstanceFromCode)  // (field_idx, Object*, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -871,7 +866,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSet64InstanceFromCode)  // (field_idx, Object*, new_val, Thread*, SP)
+    call SYMBOL(artSet64InstanceFromCode)  // (field_idx, Object*, new_val, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -892,7 +887,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSetObjInstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
+    call SYMBOL(artSetObjInstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -912,7 +907,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGet32InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
+    call SYMBOL(artGet32InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -932,7 +927,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGet64InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
+    call SYMBOL(artGet64InstanceFromCode)  // (field_idx, Object*, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -952,7 +947,7 @@
     PUSH ecx                      // pass object
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGetObjInstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
+    call SYMBOL(artGetObjInstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -972,7 +967,7 @@
     PUSH ecx                      // pass new_val
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSet32StaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
+    call SYMBOL(artSet32StaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -993,7 +988,7 @@
     PUSH ebx                      // pass referrer
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
+    call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-32)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
@@ -1013,7 +1008,7 @@
     PUSH ecx                      // pass new_val
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artSetObjStaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
+    call SYMBOL(artSetObjStaticFromCode)  // (field_idx, new_val, referrer, Thread*, SP)
     addl LITERAL(32), %esp        // pop arguments
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     RETURN_IF_EAX_ZERO            // return or deliver exception
@@ -1029,7 +1024,7 @@
     PUSH ecx                      // pass referrer
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGet32StaticFromCode)    // (field_idx, referrer, Thread*, SP)
+    call SYMBOL(artGet32StaticFromCode)    // (field_idx, referrer, Thread*, SP)
     addl LITERAL(16), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -1046,7 +1041,7 @@
     PUSH ecx                      // pass referrer
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGet64StaticFromCode)    // (field_idx, referrer, Thread*, SP)
+    call SYMBOL(artGet64StaticFromCode)    // (field_idx, referrer, Thread*, SP)
     addl LITERAL(16), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -1063,7 +1058,7 @@
     PUSH ecx                      // pass referrer
     PUSH eax                      // pass field_idx
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artGetObjStaticFromCode)   // (field_idx, referrer, Thread*, SP)
+    call SYMBOL(artGetObjStaticFromCode)   // (field_idx, referrer, Thread*, SP)
     addl LITERAL(16), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME     // restore frame up to return address
@@ -1078,7 +1073,7 @@
     PUSH ecx                      // pass receiver
     PUSH eax                      // pass proxy method
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
     punpckldq %xmm1, %xmm0
@@ -1110,7 +1105,7 @@
     PUSH ecx                      // pass receiver
     PUSH eax                      // pass method
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artQuickResolutionTrampoline) // (Method* called, receiver, Thread*, SP)
+    call SYMBOL(artQuickResolutionTrampoline) // (Method* called, receiver, Thread*, SP)
     movl %eax, %edi               // remember code pointer in EDI
     addl LITERAL(16), %esp        // pop arguments
     test %eax, %eax               // if code pointer is NULL goto deliver pending exception
@@ -1128,7 +1123,7 @@
     DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_resolution_trampoline
 
-DEFINE_FUNCTION art_quick_generic_jni_trampoline
+DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     // This also stores the native ArtMethod reference at the bottom of the stack.
 
@@ -1145,7 +1140,7 @@
     pushl %ebp                    // Pass SP (to ArtMethod).
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     SETUP_GOT_NOSAVE              // Clobbers ebx.
-    call PLT_SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
+    call SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
 
     // The C call will have registered the complete save-frame on success.
     // The result of the call is:
@@ -1175,7 +1170,7 @@
     pushl %edx                    // Pass int result.
     pushl %eax
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
-    call PLT_SYMBOL(artQuickGenericJniEndTrampoline)
+    call SYMBOL(artQuickGenericJniEndTrampoline)
 
     // Tear down the alloca.
     movl %ebp, %esp
@@ -1209,7 +1204,7 @@
     DELIVER_PENDING_EXCEPTION
 END_FUNCTION art_quick_generic_jni_trampoline
 
-DEFINE_FUNCTION art_quick_to_interpreter_bridge
+DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // save frame
     mov %esp, %edx                // remember SP
     PUSH eax                      // alignment padding
@@ -1218,7 +1213,7 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass  method
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
+    call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
     punpckldq %xmm1, %xmm0
@@ -1245,7 +1240,7 @@
     PUSH ecx                      // Pass receiver.
     PUSH eax                      // Pass Method*.
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
     addl LITERAL(28), %esp        // Pop arguments upto saved Method*.
     movl 28(%esp), %edi           // Restore edi.
     movl %eax, 28(%esp)           // Place code* over edi, just under return pc.
@@ -1280,7 +1275,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current.
     CFI_ADJUST_CFA_OFFSET(4)
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP, gpr_result, fpr_result)
+    call SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP, gpr_result, fpr_result)
     mov   %eax, %ecx              // Move returned link register.
     addl LITERAL(32), %esp        // Pop arguments.
     CFI_ADJUST_CFA_OFFSET(-32)
@@ -1310,7 +1305,7 @@
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
     CFI_ADJUST_CFA_OFFSET(4)
     SETUP_GOT_NOSAVE              // clobbers EBX
-    call PLT_SYMBOL(artDeoptimize)  // artDeoptimize(Thread*, SP)
+    call SYMBOL(artDeoptimize)  // artDeoptimize(Thread*, SP)
     int3                          // Unreachable.
 END_FUNCTION art_quick_deoptimize
 
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index 682ba43..4ae61a2 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -107,6 +107,13 @@
     #define PLT_SYMBOL(name) _ ## name
 #endif
 
+// Directive to hide a function symbol.
+#if defined(__APPLE__)
+    #define ASM_HIDDEN .private_extern
+#else
+    #define ASM_HIDDEN .hidden
+#endif
+
     /* Cache alignment for function entry */
 MACRO0(ALIGN_FUNCTION_ENTRY)
     .balign 16
@@ -116,13 +123,20 @@
 // for mac builds.
 MACRO1(DEFINE_FUNCTION, c_name)
     FUNCTION_TYPE(\c_name, 0)
+    ASM_HIDDEN VAR(c_name, 0)
     .globl VAR(c_name, 0)
     ALIGN_FUNCTION_ENTRY
 VAR(c_name, 0):
-#if !defined(__APPLE__)
-    // Have a local entrypoint that's not globl
-VAR(c_name, 0)_local:
-#endif
+    CFI_STARTPROC
+    // Ensure we get a sane starting CFA.
+    CFI_DEF_CFA(rsp, 8)
+END_MACRO
+
+MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name)
+    FUNCTION_TYPE(\c_name, 0)
+    .globl VAR(c_name, 0)
+    ALIGN_FUNCTION_ENTRY
+VAR(c_name, 0):
     CFI_STARTPROC
     // Ensure we get a sane starting CFA.
     CFI_DEF_CFA(rsp, 8)
@@ -147,6 +161,19 @@
 
 MACRO1(UNIMPLEMENTED,name)
     FUNCTION_TYPE(\name, 0)
+    ASM_HIDDEN VAR(c_name, 0)
+    .globl VAR(name, 0)
+    ALIGN_FUNCTION_ENTRY
+VAR(name, 0):
+    CFI_STARTPROC
+    int3
+    int3
+    CFI_ENDPROC
+    SIZE(\name, 0)
+END_MACRO
+
+MACRO1(UNIMPLEMENTED_NO_HIDE,name)
+    FUNCTION_TYPE(\name, 0)
     .globl VAR(name, 0)
     ALIGN_FUNCTION_ENTRY
 VAR(name, 0):
diff --git a/runtime/arch/x86_64/portable_entrypoints_x86_64.S b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
index 2e9d19a..7b84d17 100644
--- a/runtime/arch/x86_64/portable_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
@@ -25,4 +25,4 @@
 
 UNIMPLEMENTED art_portable_resolution_trampoline
 
-UNIMPLEMENTED art_portable_to_interpreter_bridge
+UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 48bc240..f95bd22 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -232,7 +232,7 @@
     // (Thread*, SP) setup
     movq %gs:THREAD_SELF_OFFSET, %rdi
     movq %rsp, %rsi
-    call PLT_SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
+    call SYMBOL(artDeliverPendingExceptionFromCode)  // artDeliverPendingExceptionFromCode(Thread*, SP)
     UNREACHABLE
 END_MACRO
 
@@ -242,7 +242,7 @@
     // Outgoing argument set up
     movq %rsp, %rsi                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdi  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -253,7 +253,7 @@
     // Outgoing argument set up
     movq %rsp, %rdx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(arg1, Thread*, SP)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -264,7 +264,7 @@
     // Outgoing argument set up
     movq %rsp, %rcx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)     // cxx_name(Thread*, SP)
     UNREACHABLE
     END_FUNCTION VAR(c_name, 0)
 END_MACRO
@@ -329,7 +329,7 @@
     movq %gs:THREAD_SELF_OFFSET, %rcx                      // pass Thread
     movq %rsp, %r8                                         // pass SP
 
-    call PLT_VAR(cxx_name, 1)                   // cxx_name(arg1, arg2, caller method*, Thread*, SP)
+    call VAR(cxx_name, 1)                   // cxx_name(arg1, arg2, caller method*, Thread*, SP)
                                                            // save the code pointer
     movq %rax, %rdi
     movq %rdx, %rax
@@ -643,7 +643,7 @@
     // Outgoing argument set up
     movq %rsp, %rsi                   // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)         // cxx_name(Thread*, SP)
+    call VAR(cxx_name, 1)         // cxx_name(Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     CALL_MACRO(return_macro, 2)       // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
@@ -655,7 +655,7 @@
     // Outgoing argument set up
     movq %rsp, %rdx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rsi  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)          // cxx_name(arg0, Thread*, SP)
+    call VAR(cxx_name, 1)          // cxx_name(arg0, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     CALL_MACRO(return_macro, 2)        // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
@@ -667,7 +667,7 @@
     // Outgoing argument set up
     movq %rsp, %rcx                    // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)          // cxx_name(arg0, arg1, Thread*, SP)
+    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME  // restore frame up to return address
     CALL_MACRO(return_macro, 2)       // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
@@ -679,7 +679,7 @@
     // Outgoing argument set up
     movq %rsp, %r8                     // pass SP
     movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
-    call PLT_VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, Thread*, SP)
+    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)        // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
@@ -692,7 +692,7 @@
                                        // arg0 is in rdi
     movq %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
     movq %rsp, %rcx                    // pass SP
-    call PLT_VAR(cxx_name, 1)          // cxx_name(arg0, referrer, Thread*, SP)
+    call VAR(cxx_name, 1)          // cxx_name(arg0, referrer, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)
     END_FUNCTION VAR(c_name, 0)
@@ -705,7 +705,7 @@
                                        // arg0 and arg1 are in rdi/rsi
     movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
     movq %rsp, %r8                     // pass SP
-    call PLT_VAR(cxx_name, 1)          // (arg0, arg1, referrer, Thread*, SP)
+    call VAR(cxx_name, 1)          // (arg0, arg1, referrer, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)
     END_FUNCTION VAR(c_name, 0)
@@ -718,7 +718,7 @@
                                        // arg0, arg1, and arg2 are in rdi/rsi/rdx
     movq %gs:THREAD_SELF_OFFSET, %r8    // pass Thread::Current()
     movq %rsp, %r9                     // pass SP
-    call PLT_VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP)
+    call VAR(cxx_name, 1)          // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     CALL_MACRO(return_macro, 2)        // return or deliver exception
     END_FUNCTION VAR(c_name, 0)
@@ -887,7 +887,7 @@
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
     movq %rsp, %rdx                       // pass SP
-    call PLT_SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artLockObjectFromCode)  // artLockObjectFromCode(object, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME    // restore frame up to return address
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_lock_object
@@ -913,7 +913,7 @@
     SETUP_REF_ONLY_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rsi     // pass Thread::Current()
     movq %rsp, %rdx                       // pass SP
-    call PLT_SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
+    call SYMBOL(artUnlockObjectFromCode)  // artUnlockObjectFromCode(object, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME    // restore frame up to return address
     RETURN_IF_EAX_ZERO
 END_FUNCTION art_quick_unlock_object
@@ -922,7 +922,7 @@
     PUSH rdi                          // Save args for exc
     PUSH rsi
     SETUP_FP_CALLEE_SAVE_FRAME
-    call PLT_SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
+    call SYMBOL(artIsAssignableFromCode)  // (Class* klass, Class* ref_klass)
     testq %rax, %rax
     jz 1f                             // jump forward if not assignable
     RESTORE_FP_CALLEE_SAVE_FRAME
@@ -937,7 +937,7 @@
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME  // save all registers as basis for long jump context
     mov %rsp, %rcx                    // pass SP
     mov %gs:THREAD_SELF_OFFSET, %rdx  // pass Thread::Current()
-    call PLT_SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+    call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
     int3                              // unreached
 END_FUNCTION art_quick_check_cast
 
@@ -958,8 +958,8 @@
 #else
     testl %edi, %edi
 //  testq %rdi, %rdi
-    jnz art_quick_aput_obj_with_bound_check_local
-    jmp art_quick_throw_null_pointer_exception_local
+    jnz art_quick_aput_obj_with_bound_check
+    jmp art_quick_throw_null_pointer_exception
 #endif  // __APPLE__
 END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
 
@@ -972,12 +972,12 @@
     movl ARRAY_LENGTH_OFFSET(%edi), %ecx
 //  movl ARRAY_LENGTH_OFFSET(%rdi), %ecx      // This zero-extends, so value(%rcx)=value(%ecx)
     cmpl %ecx, %esi
-    jb art_quick_aput_obj_local
+    jb art_quick_aput_obj
     mov %esi, %edi
 //  mov %rsi, %rdi
     mov %ecx, %esi
 //  mov %rcx, %rsi
-    jmp art_quick_throw_array_bounds_local
+    jmp art_quick_throw_array_bounds
 #endif  // __APPLE__
 END_FUNCTION art_quick_aput_obj_with_bound_check
 
@@ -1018,7 +1018,7 @@
     movl CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
     movq %rcx, %rdi               // Pass arg1 = array's component type.
 
-    call PLT_SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
+    call SYMBOL(artIsAssignableFromCode)  // (Class* a, Class* b)
 
     // Exception?
     testq %rax, %rax
@@ -1057,7 +1057,7 @@
     movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current().
                                             // Pass arg 1 = array.
 
-    call PLT_SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+    call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
     int3                          // unreached
 END_FUNCTION art_quick_aput_obj
 
@@ -1099,7 +1099,7 @@
                                        // field_idx is in rdi
     movq %gs:THREAD_SELF_OFFSET, %rcx  // pass Thread::Current()
     movq %rsp, %r8                     // pass SP
-    call PLT_SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
+    call SYMBOL(artSet64StaticFromCode)  // (field_idx, referrer, new_val, Thread*, SP)
     RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
     RETURN_IF_EAX_ZERO                 // return or deliver exception
 END_FUNCTION art_quick_set64_static
@@ -1139,7 +1139,7 @@
     movq %rdi, 0(%rsp)
     movq %gs:THREAD_SELF_OFFSET, %rdx  // Pass Thread::Current().
     movq %rsp, %rcx                    // Pass SP.
-    call PLT_SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
+    call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
     movq %rax, %xmm0                   // Copy return value in case of float returns.
     addq LITERAL(168 + 4*8), %rsp            // Pop arguments.
     CFI_ADJUST_CFA_OFFSET(-168 - 4*8)
@@ -1158,7 +1158,7 @@
     movl 8(%rsp), %edi            // load caller Method*
     movl METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi  // load dex_cache_resolved_methods
     movl OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi  // load the target method
-    jmp art_quick_invoke_interface_trampoline_local
+    jmp art_quick_invoke_interface_trampoline
 #endif  // __APPLE__
 END_FUNCTION art_quick_imt_conflict_trampoline
 
@@ -1166,7 +1166,7 @@
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
     movq %gs:THREAD_SELF_OFFSET, %rdx
     movq %rsp, %rcx
-    call PLT_SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
+    call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP)
     movq %rax, %r10               // Remember returned code pointer in R10.
     movq (%rsp), %rdi             // Load called method into RDI.
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -1254,7 +1254,7 @@
     /*
      * Called to do a generic JNI down-call
      */
-DEFINE_FUNCTION art_quick_generic_jni_trampoline
+DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline
     // Save callee and GPR args, mixed together to agree with core spills bitmap.
     PUSH r15  // Callee save.
     PUSH r14  // Callee save.
@@ -1310,7 +1310,7 @@
     //  gs:...   rbp      <= where they are
     movq %gs:THREAD_SELF_OFFSET, %rdi
     movq %rbp, %rsi
-    call PLT_SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
+    call SYMBOL(artQuickGenericJniTrampoline)  // (Thread*, sp)
 
     // The C call will have registered the complete save-frame on success.
     // The result of the call is:
@@ -1354,7 +1354,7 @@
     movq %gs:THREAD_SELF_OFFSET, %rdi
     movq %rax, %rsi
     movq %xmm0, %rdx
-    call PLT_SYMBOL(artQuickGenericJniEndTrampoline)
+    call SYMBOL(artQuickGenericJniEndTrampoline)
 
     // Tear down the alloca.
     movq %rbp, %rsp
@@ -1441,11 +1441,11 @@
      * RDI = method being called / to bridge to.
      * RSI, RDX, RCX, R8, R9 are arguments to that method.
      */
-DEFINE_FUNCTION art_quick_to_interpreter_bridge
+DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge
     SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME   // Set up frame and save arguments.
     movq %gs:THREAD_SELF_OFFSET, %rsi      // RSI := Thread::Current()
     movq %rsp, %rdx                        // RDX := sp
-    call PLT_SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
+    call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME  // TODO: no need to restore arguments in this case.
     movq %rax, %xmm0                   // Place return value also into floating point return value.
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
@@ -1467,12 +1467,12 @@
     movq %rsp, %rcx                     // Pass SP.
     movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %r8   // Pass return PC.
 
-    call PLT_SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
 
                                   // %rax = result of call.
     movq %r12, %rdi               // Reload method pointer.
 
-    leaq art_quick_instrumentation_exit_local(%rip), %r12   // Set up return through instrumentation
+    leaq art_quick_instrumentation_exit(%rip), %r12   // Set up return through instrumentation
     movq %r12, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp) // exit.
 
     RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -1501,7 +1501,7 @@
     movq  %rax, %rdx                          // Pass integer result.
     movq  %xmm0, %rcx                         // Pass floating-point result.
 
-    call PLT_SYMBOL(artInstrumentationMethodExitFromCode)   // (Thread*, SP, gpr_res, fpr_res)
+    call SYMBOL(artInstrumentationMethodExitFromCode)   // (Thread*, SP, gpr_res, fpr_res)
 
     movq  %rax, %rdi          // Store return PC
     movq  %rdx, %rsi          // Store second return PC in hidden arg.
@@ -1526,7 +1526,7 @@
                                    // Stack should be aligned now.
     movq %rsp, %rsi                           // Pass SP.
     movq %gs:THREAD_SELF_OFFSET, %rdi         // Pass Thread.
-    call PLT_SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP)
+    call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP)
     int3                           // Unreachable.
 END_FUNCTION art_quick_deoptimize
 
@@ -1577,7 +1577,7 @@
 
 DEFINE_FUNCTION art_quick_assignable_from_code
     SETUP_FP_CALLEE_SAVE_FRAME
-    call PLT_SYMBOL(artIsAssignableFromCode)       // (const mirror::Class*, const mirror::Class*)
+    call SYMBOL(artIsAssignableFromCode)       // (const mirror::Class*, const mirror::Class*)
     RESTORE_FP_CALLEE_SAVE_FRAME
     ret
 END_FUNCTION art_quick_assignable_from_code
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
index 4f7753d..3469eca 100644
--- a/runtime/base/allocator.cc
+++ b/runtime/base/allocator.cc
@@ -23,7 +23,7 @@
 
 namespace art {
 
-class MallocAllocator : public Allocator {
+class MallocAllocator FINAL : public Allocator {
  public:
   explicit MallocAllocator() {}
   ~MallocAllocator() {}
@@ -42,7 +42,7 @@
 
 MallocAllocator g_malloc_allocator;
 
-class NoopAllocator : public Allocator {
+class NoopAllocator FINAL : public Allocator {
  public:
   explicit NoopAllocator() {}
   ~NoopAllocator() {}
diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h
new file mode 100644
index 0000000..dc13dd5
--- /dev/null
+++ b/runtime/base/bit_vector-inl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+#define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+
+#include "bit_vector.h"
+#include "logging.h"
+#include "utils.h"
+
+namespace art {
+
+inline bool BitVector::IndexIterator::operator==(const IndexIterator& other) const {
+  DCHECK(bit_storage_ == other.bit_storage_);
+  DCHECK_EQ(storage_size_, other.storage_size_);
+  return bit_index_ == other.bit_index_;
+}
+
+inline int BitVector::IndexIterator::operator*() const {
+  DCHECK_LT(bit_index_, BitSize());
+  return bit_index_;
+}
+
+inline BitVector::IndexIterator& BitVector::IndexIterator::operator++() {
+  DCHECK_LT(bit_index_, BitSize());
+  bit_index_ = FindIndex(bit_index_ + 1u);
+  return *this;
+}
+
+inline BitVector::IndexIterator BitVector::IndexIterator::operator++(int) {
+  IndexIterator result(*this);
+  ++*this;
+  return result;
+}
+
+inline uint32_t BitVector::IndexIterator::FindIndex(uint32_t start_index) const {
+  DCHECK_LE(start_index, BitSize());
+  uint32_t word_index = start_index / kWordBits;
+  if (UNLIKELY(word_index == storage_size_)) {
+    return start_index;
+  }
+  uint32_t word = bit_storage_[word_index];
+  // Mask out any bits in the first word we've already considered.
+  word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
+  while (word == 0u) {
+    ++word_index;
+    if (UNLIKELY(word_index == storage_size_)) {
+      return BitSize();
+    }
+    word = bit_storage_[word_index];
+  }
+  return word_index * 32u + CTZ(word);
+}
+
+inline void BitVector::ClearAllBits() {
+  memset(storage_, 0, storage_size_ * kWordBytes);
+}
+
+inline bool BitVector::Equal(const BitVector* src) const {
+  return (storage_size_ == src->GetStorageSize()) &&
+    (expandable_ == src->IsExpandable()) &&
+    (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 1b9022e..3d2f0de 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -16,20 +16,14 @@
 
 #include "bit_vector.h"
 
+#include "allocator.h"
+#include "bit_vector-inl.h"
+
 namespace art {
 
-// TODO: profile to make sure this is still a win relative to just using shifted masks.
-static uint32_t check_masks[32] = {
-  0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
-  0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
-  0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
-  0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
-  0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
-  0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
-  0x40000000, 0x80000000 };
-
-static inline uint32_t BitsToWords(uint32_t bits) {
-  return (bits + 31) >> 5;
+// The number of words necessary to encode bits.
+static constexpr uint32_t BitsToWords(uint32_t bits) {
+  return RoundUp(bits, 32) / 32;
 }
 
 // TODO: replace excessive argument defaulting when we are at gcc 4.7
@@ -40,10 +34,10 @@
                      Allocator* allocator,
                      uint32_t storage_size,
                      uint32_t* storage)
-  : allocator_(allocator),
-    expandable_(expandable),
+  : storage_(storage),
     storage_size_(storage_size),
-    storage_(storage) {
+    allocator_(allocator),
+    expandable_(expandable) {
   COMPILE_ASSERT(sizeof(*storage_) == kWordBytes, check_word_bytes);
   COMPILE_ASSERT(sizeof(*storage_) * 8u == kWordBits, check_word_bits);
   if (storage_ == nullptr) {
@@ -56,59 +50,7 @@
   allocator_->Free(storage_);
 }
 
-/*
- * Determine whether or not the specified bit is set.
- */
-bool BitVector::IsBitSet(uint32_t num) const {
-  // If the index is over the size:
-  if (num >= storage_size_ * kWordBits) {
-    // Whether it is expandable or not, this bit does not exist: thus it is not set.
-    return false;
-  }
-
-  return IsBitSet(storage_, num);
-}
-
-// Mark all bits bit as "clear".
-void BitVector::ClearAllBits() {
-  memset(storage_, 0, storage_size_ * kWordBytes);
-}
-
-// Mark the specified bit as "set".
-/*
- * TUNING: this could have pathologically bad growth/expand behavior.  Make sure we're
- * not using it badly or change resize mechanism.
- */
-void BitVector::SetBit(uint32_t num) {
-  if (num >= storage_size_ * kWordBits) {
-    DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
-
-    /* Round up to word boundaries for "num+1" bits */
-    uint32_t new_size = BitsToWords(num + 1);
-    DCHECK_GT(new_size, storage_size_);
-    uint32_t *new_storage =
-        static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
-    memcpy(new_storage, storage_, storage_size_ * kWordBytes);
-    // Zero out the new storage words.
-    memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
-    // TOTO: collect stats on space wasted because of resize.
-    storage_ = new_storage;
-    storage_size_ = new_size;
-  }
-
-  storage_[num >> 5] |= check_masks[num & 0x1f];
-}
-
-// Mark the specified bit as "unset".
-void BitVector::ClearBit(uint32_t num) {
-  // If the index is over the size, we don't have to do anything, it is cleared.
-  if (num < storage_size_ * kWordBits) {
-    // Otherwise, go ahead and clear it.
-    storage_[num >> 5] &= ~check_masks[num & 0x1f];
-  }
-}
-
-bool BitVector::SameBitsSet(const BitVector *src) {
+bool BitVector::SameBitsSet(const BitVector *src) const {
   int our_highest = GetHighestBitSet();
   int src_highest = src->GetHighestBitSet();
 
@@ -134,7 +76,6 @@
   return (memcmp(storage_, src->GetRawStorage(), our_highest_index * kWordBytes) == 0);
 }
 
-// Intersect with another bit vector.
 void BitVector::Intersect(const BitVector* src) {
   uint32_t src_storage_size = src->storage_size_;
 
@@ -155,9 +96,6 @@
   }
 }
 
-/*
- * Union with another bit vector.
- */
 bool BitVector::Union(const BitVector* src) {
   // Get the highest bit to determine how much we need to expand.
   int highest_bit = src->GetHighestBitSet();
@@ -175,8 +113,7 @@
   if (storage_size_ < src_size) {
     changed = true;
 
-    // Set it to reallocate.
-    SetBit(highest_bit);
+    EnsureSize(highest_bit);
 
     // Paranoid: storage size should be big enough to hold this bit now.
     DCHECK_LT(static_cast<uint32_t> (highest_bit), storage_size_ * kWordBits);
@@ -242,21 +179,20 @@
 }
 
 void BitVector::Subtract(const BitVector *src) {
-    uint32_t src_size = src->storage_size_;
+  uint32_t src_size = src->storage_size_;
 
-    // We only need to operate on bytes up to the smaller of the sizes of the two operands.
-    unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
+  // We only need to operate on bytes up to the smaller of the sizes of the two operands.
+  unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
 
-    // Difference until max, we know both accept it:
-    //   There is no need to do more:
-    //     If we are bigger than src, the upper bits are unchanged.
-    //     If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
-    for (uint32_t idx = 0; idx < min_size; idx++) {
-        storage_[idx] &= (~(src->GetRawStorageWord(idx)));
-    }
+  // Difference until max, we know both accept it:
+  //   There is no need to do more:
+  //     If we are bigger than src, the upper bits are unchanged.
+  //     If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
+  for (uint32_t idx = 0; idx < min_size; idx++) {
+    storage_[idx] &= (~(src->GetRawStorageWord(idx)));
+  }
 }
 
-// Count the number of bits that are set.
 uint32_t BitVector::NumSetBits() const {
   uint32_t count = 0;
   for (uint32_t word = 0; word < storage_size_; word++) {
@@ -265,17 +201,11 @@
   return count;
 }
 
-// Count the number of bits that are set in range [0, end).
 uint32_t BitVector::NumSetBits(uint32_t end) const {
   DCHECK_LE(end, storage_size_ * kWordBits);
   return NumSetBits(storage_, end);
 }
 
-/*
- * Mark specified number of bits as "set". Cannot set all bits like ClearAll
- * since there might be unused bits - setting those to one will confuse the
- * iterator.
- */
 void BitVector::SetInitialBits(uint32_t num_bits) {
   // If num_bits is 0, clear everything.
   if (num_bits == 0) {
@@ -288,7 +218,7 @@
 
   uint32_t idx;
   // We can set every storage element with -1.
-  for (idx = 0; idx < (num_bits >> 5); idx++) {
+  for (idx = 0; idx < WordIndex(num_bits); idx++) {
     storage_[idx] = -1;
   }
 
@@ -312,20 +242,8 @@
     uint32_t value = storage_[idx];
 
     if (value != 0) {
-      // Shift right for the counting.
-      value /= 2;
-
-      int cnt = 0;
-
-      // Count the bits.
-      while (value > 0) {
-        value /= 2;
-        cnt++;
-      }
-
-      // Return cnt + how many storage units still remain * the number of bits per unit.
-      int res = cnt + (idx * kWordBits);
-      return res;
+      // Return highest bit set in value plus bits from previous storage indexes.
+      return 31 - CLZ(value) + (idx * kWordBits);
     }
   }
 
@@ -333,23 +251,6 @@
   return -1;
 }
 
-bool BitVector::EnsureSizeAndClear(unsigned int num) {
-  // Check if the bitvector is expandable.
-  if (IsExpandable() == false) {
-    return false;
-  }
-
-  if (num > 0) {
-    // Now try to expand by setting the last bit.
-    SetBit(num - 1);
-  }
-
-  // We must clear all bits as per our specification.
-  ClearAllBits();
-
-  return true;
-}
-
 void BitVector::Copy(const BitVector *src) {
   // Get highest bit set, we only need to copy till then.
   int highest_bit = src->GetHighestBitSet();
@@ -375,13 +276,8 @@
   }
 }
 
-bool BitVector::IsBitSet(const uint32_t* storage, uint32_t num) {
-  uint32_t val = storage[num >> 5] & check_masks[num & 0x1f];
-  return (val != 0);
-}
-
 uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
-  uint32_t word_end = end >> 5;
+  uint32_t word_end = WordIndex(end);
   uint32_t partial_word_bits = end & 0x1f;
 
   uint32_t count = 0u;
@@ -400,45 +296,6 @@
   os << buffer.str() << std::endl;
 }
 
-
-void BitVector::DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const {
-  // Now print it to the file.
-  fprintf(file, "    {%s}", buffer.str().c_str());
-
-  // If it isn't the last entry, add a |.
-  if (last_entry == false) {
-    fprintf(file, "|");
-  }
-
-  // Add the \n.
-  fprintf(file, "\\\n");
-}
-
-void BitVector::DumpDot(FILE* file, const char* prefix, bool last_entry) const {
-  std::ostringstream buffer;
-  DumpHelper(prefix, buffer);
-  DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesDot(FILE* file, const char* prefix, bool last_entry) const {
-  std::ostringstream buffer;
-  DumpIndicesHelper(prefix, buffer);
-  DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const {
-  // Initialize it.
-  if (prefix != nullptr) {
-    buffer << prefix;
-  }
-
-  for (size_t i = 0; i < storage_size_ * kWordBits; i++) {
-    if (IsBitSet(i)) {
-      buffer << i << " ";
-    }
-  }
-}
-
 void BitVector::DumpHelper(const char* prefix, std::ostringstream& buffer) const {
   // Initialize it.
   if (prefix != nullptr) {
@@ -452,4 +309,22 @@
   buffer << ')';
 }
 
+void BitVector::EnsureSize(uint32_t idx) {
+  if (idx >= storage_size_ * kWordBits) {
+    DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << idx;
+
+    /* Round up to word boundaries for "idx+1" bits */
+    uint32_t new_size = BitsToWords(idx + 1);
+    DCHECK_GT(new_size, storage_size_);
+    uint32_t *new_storage =
+        static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
+    memcpy(new_storage, storage_, storage_size_ * kWordBytes);
+    // Zero out the new storage words.
+    memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
+    // TOTO: collect stats on space wasted because of resize.
+    storage_ = new_storage;
+    storage_size_ = new_size;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index fb1646f..1e28a27 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -18,235 +18,237 @@
 #define ART_RUNTIME_BASE_BIT_VECTOR_H_
 
 #include <stdint.h>
-#include <stddef.h>
-
-#include "allocator.h"
-#include "base/logging.h"
-#include "utils.h"
+#include <iterator>
 
 namespace art {
 
+class Allocator;
+
 /*
  * Expanding bitmap, used for tracking resources.  Bits are numbered starting
  * from zero.  All operations on a BitVector are unsynchronized.
  */
 class BitVector {
-  public:
-    class IndexContainer;
+ public:
+  class IndexContainer;
 
-    /**
-     * @brief Convenient iterator across the indexes of the BitVector's set bits.
-     *
-     * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
-     * to the highest index of the BitVector's set bits. Instances can be retrieved
-     * only through BitVector::Indexes() which returns an IndexContainer wrapper
-     * object with begin() and end() suitable for range-based loops:
-     *   for (uint32_t idx : bit_vector.Indexes()) {
-     *     // Use idx.
-     *   }
-     */
-    class IndexIterator
-        : std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
-      public:
-        bool operator==(const IndexIterator& other) const {
-          DCHECK(bit_storage_ == other.bit_storage_);
-          DCHECK_EQ(storage_size_, other.storage_size_);
-          return bit_index_ == other.bit_index_;
-        }
+  /**
+   * @brief Convenient iterator across the indexes of the BitVector's set bits.
+   *
+   * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
+   * to the highest index of the BitVector's set bits. Instances can be retrieved
+   * only through BitVector::Indexes() which returns an IndexContainer wrapper
+   * object with begin() and end() suitable for range-based loops:
+   *   for (uint32_t idx : bit_vector.Indexes()) {
+   *     // Use idx.
+   *   }
+   */
+  class IndexIterator :
+      std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
+   public:
+    bool operator==(const IndexIterator& other) const;
 
-        bool operator!=(const IndexIterator& other) const {
-          return !(*this == other);
-        }
-
-        int operator*() const {
-          DCHECK_LT(bit_index_, BitSize());
-          return bit_index_;
-        }
-
-        IndexIterator& operator++() {
-          DCHECK_LT(bit_index_, BitSize());
-          bit_index_ = FindIndex(bit_index_ + 1u);
-          return *this;
-        }
-
-        IndexIterator operator++(int) {
-          IndexIterator result(*this);
-          ++*this;
-          return result;
-        }
-
-        // Helper function to check for end without comparing with bit_vector.Indexes().end().
-        bool Done() const {
-          return bit_index_ == BitSize();
-        }
-
-      private:
-        struct begin_tag { };
-        struct end_tag { };
-
-        IndexIterator(const BitVector* bit_vector, begin_tag)
-          : bit_storage_(bit_vector->GetRawStorage()),
-            storage_size_(bit_vector->storage_size_),
-            bit_index_(FindIndex(0u)) { }
-
-        IndexIterator(const BitVector* bit_vector, end_tag)
-          : bit_storage_(bit_vector->GetRawStorage()),
-            storage_size_(bit_vector->storage_size_),
-            bit_index_(BitSize()) { }
-
-        uint32_t BitSize() const {
-          return storage_size_ * kWordBits;
-        }
-
-        uint32_t FindIndex(uint32_t start_index) const {
-          DCHECK_LE(start_index, BitSize());
-          uint32_t word_index = start_index / kWordBits;
-          if (UNLIKELY(word_index == storage_size_)) {
-            return start_index;
-          }
-          uint32_t word = bit_storage_[word_index];
-          // Mask out any bits in the first word we've already considered.
-          word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
-          while (word == 0u) {
-            ++word_index;
-            if (UNLIKELY(word_index == storage_size_)) {
-              return BitSize();
-            }
-            word = bit_storage_[word_index];
-          }
-          return word_index * 32u + CTZ(word);
-        }
-
-        const uint32_t* const bit_storage_;
-        const uint32_t storage_size_;  // Size of vector in words.
-        uint32_t bit_index_;           // Current index (size in bits).
-
-        friend class BitVector::IndexContainer;
-    };
-
-    /**
-     * @brief BitVector wrapper class for iteration across indexes of set bits.
-     */
-    class IndexContainer {
-     public:
-      explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
-
-      IndexIterator begin() const {
-        return IndexIterator(bit_vector_, IndexIterator::begin_tag());
-      }
-
-      IndexIterator end() const {
-        return IndexIterator(bit_vector_, IndexIterator::end_tag());
-      }
-
-     private:
-      const BitVector* const bit_vector_;
-    };
-
-    BitVector(uint32_t start_bits,
-              bool expandable,
-              Allocator* allocator,
-              uint32_t storage_size = 0,
-              uint32_t* storage = nullptr);
-
-    virtual ~BitVector();
-
-    void SetBit(uint32_t num);
-    void ClearBit(uint32_t num);
-    bool IsBitSet(uint32_t num) const;
-    void ClearAllBits();
-    void SetInitialBits(uint32_t num_bits);
-
-    void Copy(const BitVector* src);
-    void Intersect(const BitVector* src2);
-    bool Union(const BitVector* src);
-
-    // Set bits of union_with that are not in not_in.
-    bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
-
-    void Subtract(const BitVector* src);
-    // Are we equal to another bit vector?  Note: expandability attributes must also match.
-    bool Equal(const BitVector* src) {
-      return (storage_size_ == src->GetStorageSize()) &&
-        (expandable_ == src->IsExpandable()) &&
-        (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+    bool operator!=(const IndexIterator& other) const {
+      return !(*this == other);
     }
 
-    /**
-     * @brief Are all the bits set the same?
-     * @details expandability and size can differ as long as the same bits are set.
-     */
-    bool SameBitsSet(const BitVector *src);
+    int operator*() const;
 
-    uint32_t NumSetBits() const;
+    IndexIterator& operator++();
 
-    // Number of bits set in range [0, end).
-    uint32_t NumSetBits(uint32_t end) const;
+    IndexIterator operator++(int);
 
-    IndexContainer Indexes() const {
-      return IndexContainer(this);
+    // Helper function to check for end without comparing with bit_vector.Indexes().end().
+    bool Done() const {
+      return bit_index_ == BitSize();
     }
 
-    uint32_t GetStorageSize() const { return storage_size_; }
-    bool IsExpandable() const { return expandable_; }
-    uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
-    uint32_t* GetRawStorage() { return storage_; }
-    const uint32_t* GetRawStorage() const { return storage_; }
-    size_t GetSizeOf() const { return storage_size_ * kWordBytes; }
+   private:
+    struct begin_tag { };
+    struct end_tag { };
 
-    /**
-     * @return the highest bit set, -1 if none are set
+    IndexIterator(const BitVector* bit_vector, begin_tag)
+      : bit_storage_(bit_vector->GetRawStorage()),
+        storage_size_(bit_vector->storage_size_),
+        bit_index_(FindIndex(0u)) { }
+
+    IndexIterator(const BitVector* bit_vector, end_tag)
+      : bit_storage_(bit_vector->GetRawStorage()),
+        storage_size_(bit_vector->storage_size_),
+        bit_index_(BitSize()) { }
+
+    uint32_t BitSize() const {
+      return storage_size_ * kWordBits;
+    }
+
+    uint32_t FindIndex(uint32_t start_index) const;
+    const uint32_t* const bit_storage_;
+    const uint32_t storage_size_;  // Size of vector in words.
+    uint32_t bit_index_;           // Current index (size in bits).
+
+    friend class BitVector::IndexContainer;
+  };
+
+  /**
+   * @brief BitVector wrapper class for iteration across indexes of set bits.
+   */
+  class IndexContainer {
+   public:
+    explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
+
+    IndexIterator begin() const {
+      return IndexIterator(bit_vector_, IndexIterator::begin_tag());
+    }
+
+    IndexIterator end() const {
+      return IndexIterator(bit_vector_, IndexIterator::end_tag());
+    }
+
+   private:
+    const BitVector* const bit_vector_;
+  };
+
+  BitVector(uint32_t start_bits,
+            bool expandable,
+            Allocator* allocator,
+            uint32_t storage_size = 0,
+            uint32_t* storage = nullptr);
+
+  virtual ~BitVector();
+
+  // Mark the specified bit as "set".
+  void SetBit(uint32_t idx) {
+    /*
+     * TUNING: this could have pathologically bad growth/expand behavior.  Make sure we're
+     * not using it badly or change resize mechanism.
      */
-    int GetHighestBitSet() const;
+    if (idx >= storage_size_ * kWordBits) {
+      EnsureSize(idx);
+    }
+    storage_[WordIndex(idx)] |= BitMask(idx);
+  }
 
-    // Is bit set in storage. (No range check.)
-    static bool IsBitSet(const uint32_t* storage, uint32_t num);
-    // Number of bits set in range [0, end) in storage. (No range check.)
-    static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+  // Mark the specified bit as "unset".
+  void ClearBit(uint32_t idx) {
+    // If the index is over the size, we don't have to do anything, it is cleared.
+    if (idx < storage_size_ * kWordBits) {
+      // Otherwise, go ahead and clear it.
+      storage_[WordIndex(idx)] &= ~BitMask(idx);
+    }
+  }
 
-    bool EnsureSizeAndClear(unsigned int num);
+  // Determine whether or not the specified bit is set.
+  bool IsBitSet(uint32_t idx) const {
+    // If the index is over the size, whether it is expandable or not, this bit does not exist:
+    // thus it is not set.
+    return (idx < (storage_size_ * kWordBits)) && IsBitSet(storage_, idx);
+  }
 
-    void Dump(std::ostream& os, const char* prefix) const;
+  // Mark all bits bit as "clear".
+  void ClearAllBits();
 
-    /**
-     * @brief last_entry is this the last entry for the dot dumping
-     * @details if not, a "|" is appended to the dump.
-     */
-    void DumpDot(FILE* file, const char* prefix, bool last_entry = false) const;
+  // Mark specified number of bits as "set". Cannot set all bits like ClearAll since there might
+  // be unused bits - setting those to one will confuse the iterator.
+  void SetInitialBits(uint32_t num_bits);
 
-    /**
-     * @brief last_entry is this the last entry for the dot dumping
-     * @details if not, a "|" is appended to the dump.
-     */
-    void DumpIndicesDot(FILE* file, const char* prefix, bool last_entry = false) const;
+  void Copy(const BitVector* src);
 
-  protected:
-    /**
-     * @brief Dump the bitvector into buffer in a 00101..01 format.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+  // Intersect with another bit vector.
+  void Intersect(const BitVector* src2);
 
-    /**
-     * @brief Dump the bitvector in a 1 2 5 8 format, where the numbers are the bit set.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const;
+  // Union with another bit vector.
+  bool Union(const BitVector* src);
 
-    /**
-     * @brief Wrapper to perform the bitvector dumping with the .dot format.
-     * @param buffer the ostringstream used to dump the bitvector into.
-     */
-    void DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const;
+  // Set bits of union_with that are not in not_in.
+  bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
 
-  private:
-    static constexpr uint32_t kWordBytes = sizeof(uint32_t);
-    static constexpr uint32_t kWordBits = kWordBytes * 8;
+  void Subtract(const BitVector* src);
 
-    Allocator* const allocator_;
-    const bool expandable_;         // expand bitmap if we run out?
-    uint32_t   storage_size_;       // current size, in 32-bit words.
-    uint32_t*  storage_;
+  // Are we equal to another bit vector?  Note: expandability attributes must also match.
+  bool Equal(const BitVector* src) const;
+
+  /**
+   * @brief Are all the bits set the same?
+   * @details expandability and size can differ as long as the same bits are set.
+   */
+  bool SameBitsSet(const BitVector *src) const;
+
+  // Count the number of bits that are set.
+  uint32_t NumSetBits() const;
+
+  // Count the number of bits that are set in range [0, end).
+  uint32_t NumSetBits(uint32_t end) const;
+
+  IndexContainer Indexes() const {
+    return IndexContainer(this);
+  }
+
+  uint32_t GetStorageSize() const {
+    return storage_size_;
+  }
+
+  bool IsExpandable() const {
+    return expandable_;
+  }
+
+  uint32_t GetRawStorageWord(size_t idx) const {
+    return storage_[idx];
+  }
+
+  uint32_t* GetRawStorage() {
+    return storage_;
+  }
+
+  const uint32_t* GetRawStorage() const {
+    return storage_;
+  }
+
+  size_t GetSizeOf() const {
+    return storage_size_ * kWordBytes;
+  }
+
+  /**
+   * @return the highest bit set, -1 if none are set
+   */
+  int GetHighestBitSet() const;
+
+  // Is bit set in storage. (No range check.)
+  static bool IsBitSet(const uint32_t* storage, uint32_t idx) {
+    return (storage[WordIndex(idx)] & BitMask(idx)) != 0;
+  }
+
+  // Number of bits set in range [0, end) in storage. (No range check.)
+  static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+
+  void Dump(std::ostream& os, const char* prefix) const;
+
+ private:
+  /**
+   * @brief Dump the bitvector into buffer in a 00101..01 format.
+   * @param buffer the ostringstream used to dump the bitvector into.
+   */
+  void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+
+  // Ensure there is space for a bit at idx.
+  void EnsureSize(uint32_t idx);
+
+  // The index of the word within storage.
+  static constexpr uint32_t WordIndex(uint32_t idx) {
+    return idx >> 5;
+  }
+
+  // A bit mask to extract the bit for the given index.
+  static constexpr uint32_t BitMask(uint32_t idx) {
+    return 1 << (idx & 0x1f);
+  }
+
+  static constexpr uint32_t kWordBytes = sizeof(uint32_t);
+  static constexpr uint32_t kWordBits = kWordBytes * 8;
+
+  uint32_t*  storage_;            // The storage for the bit vector.
+  uint32_t   storage_size_;       // Current size, in 32-bit words.
+  Allocator* const allocator_;    // Allocator if expandable.
+  const bool expandable_;         // Should the bitmap expand if too small?
 };
 
 
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index 1403f50..df5d79d 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -16,7 +16,8 @@
 
 #include <memory>
 
-#include "bit_vector.h"
+#include "allocator.h"
+#include "bit_vector-inl.h"
 #include "gtest/gtest.h"
 
 namespace art {
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 99277a0..b0f8e22 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -228,7 +228,10 @@
     }
     if (invoke != kStatic) {
       mirror::Object* o = soa.Decode<mirror::Object*>(jobj);
-      if (!o->InstanceOf(m->GetDeclaringClass())) {
+      if (o == nullptr) {
+        AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+        return false;
+      } else if (!o->InstanceOf(m->GetDeclaringClass())) {
         AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
         return false;
       }
@@ -292,7 +295,10 @@
       return false;
     }
     mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
-    if (!o->InstanceOf(m->GetDeclaringClass())) {
+    if (o == nullptr) {
+      AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+      return false;
+    } else if (!o->InstanceOf(m->GetDeclaringClass())) {
       AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
       return false;
     }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f31e273..8fc1d07 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -851,7 +851,8 @@
       // We opened the oat file, so we must register it.
       RegisterOatFile(oat_file);
     }
-    return true;
+    // If the file isn't executable we failed patchoat but did manage to get the dex files.
+    return oat_file->IsExecutable();
   } else {
     if (needs_registering) {
       // We opened it, delete it.
@@ -1136,11 +1137,18 @@
     error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
                                        dex_location));
     return nullptr;
-  } else if (!VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+  } else if (oat_file->IsExecutable() &&
+             !VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
     error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
                                        "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
                                        error_msg.c_str()));
     return nullptr;
+  } else if (!oat_file->IsExecutable() &&
+             !VerifyOatImageChecksum(oat_file.get(), isa)) {
+    error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
+                                       "dex location '%s'. Image checksum incorrect.",
+                                       oat_file->GetLocation().c_str(), dex_location));
+    return nullptr;
   } else {
     return oat_file.release();
   }
@@ -1310,11 +1318,35 @@
   return ret;
 }
 
+const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
+                                                  InstructionSet isa,
+                                                  std::string* error_msg) {
+  // We open it non-executable
+  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, NULL, false, error_msg));
+  if (output.get() == nullptr) {
+    return nullptr;
+  }
+  if (VerifyOatImageChecksum(output.get(), isa)) {
+    return output.release();
+  } else {
+    *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
+                              oat_path.c_str());
+    return nullptr;
+  }
+}
+
 const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
                                                 const std::string& output_oat,
                                                 const std::string& image_location,
                                                 InstructionSet isa,
                                                 std::string* error_msg) {
+  if (!Runtime::Current()->IsDex2OatEnabled()) {
+    // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
+    // input_oat but make sure we only do interpretation on it's dex files.
+    LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
+                 << "disabled. Attempting to use oat file for interpretation";
+    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
+  }
   Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
   std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
 
@@ -1352,6 +1384,12 @@
                                 "but was unable to open output file '%s': %s",
                                 input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
     }
+  } else if (!Runtime::Current()->IsCompiler()) {
+    // patchoat failed which means we probably don't have enough room to place the output oat file,
+    // instead of failing we should just run the interpreter from the dex files in the input oat.
+    LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
+                 << "for interpretation. patchoat failure was: " << *error_msg;
+    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
   } else {
     *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
                               "failed: %s", input_oat.c_str(), output_oat.c_str(),
@@ -4348,7 +4386,7 @@
       return true;
     }
   }
-  StackHandleScope<4> hs(self);
+  StackHandleScope<5> hs(self);
   Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
   if (UNLIKELY(iftable.Get() == NULL)) {
     CHECK(self->IsExceptionPending());  // OOME.
@@ -4431,7 +4469,13 @@
   }
   MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
   MethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
-  std::vector<mirror::ArtMethod*> miranda_list;
+  size_t max_miranda_methods = 0;  // The max size of miranda_list.
+  for (size_t i = 0; i < ifcount; ++i) {
+    max_miranda_methods += iftable->GetInterface(i)->NumVirtualMethods();
+  }
+  Handle<mirror::ObjectArray<mirror::ArtMethod>>
+      miranda_list(hs.NewHandle(AllocArtMethodArray(self, max_miranda_methods)));
+  size_t miranda_list_size = 0;  // The current size of miranda_list.
   for (size_t i = 0; i < ifcount; ++i) {
     size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
     if (num_methods > 0) {
@@ -4446,8 +4490,7 @@
       Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
           hs.NewHandle(klass->GetVTableDuringLinking()));
       for (size_t j = 0; j < num_methods; ++j) {
-        mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j);
-        interface_mh.ChangeMethod(interface_method);
+        interface_mh.ChangeMethod(iftable->GetInterface(i)->GetVirtualMethod(j));
         int32_t k;
         // For each method listed in the interface's method list, find the
         // matching method in our class's method list.  We want to favor the
@@ -4458,22 +4501,21 @@
         // those don't end up in the virtual method table, so it shouldn't
         // matter which direction we go.  We walk it backward anyway.)
         for (k = vtable->GetLength() - 1; k >= 0; --k) {
-          mirror::ArtMethod* vtable_method = vtable->Get(k);
-          vtable_mh.ChangeMethod(vtable_method);
+          vtable_mh.ChangeMethod(vtable->Get(k));
           if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
-            if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
+            if (!vtable_mh.Get()->IsAbstract() && !vtable_mh.Get()->IsPublic()) {
               ThrowIllegalAccessError(
                   klass.Get(),
                   "Method '%s' implementing interface method '%s' is not public",
-                  PrettyMethod(vtable_method).c_str(),
-                  PrettyMethod(interface_method).c_str());
+                  PrettyMethod(vtable_mh.Get()).c_str(),
+                  PrettyMethod(interface_mh.Get()).c_str());
               return false;
             }
-            method_array->Set<false>(j, vtable_method);
+            method_array->Set<false>(j, vtable_mh.Get());
             // Place method in imt if entry is empty, place conflict otherwise.
-            uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+            uint32_t imt_index = interface_mh.Get()->GetDexMethodIndex() % mirror::Class::kImtSize;
             if (imtable->Get(imt_index) == NULL) {
-              imtable->Set<false>(imt_index, vtable_method);
+              imtable->Set<false>(imt_index, vtable_mh.Get());
               imtable_changed = true;
             } else {
               imtable->Set<false>(imt_index, runtime->GetImtConflictMethod());
@@ -4484,7 +4526,9 @@
         if (k < 0) {
           StackHandleScope<1> hs(self);
           auto miranda_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
-          for (mirror::ArtMethod* mir_method : miranda_list) {
+          for (size_t l = 0; l < miranda_list_size; ++l) {
+            mirror::ArtMethod* mir_method = miranda_list->Get(l);
+            DCHECK(mir_method != nullptr);
             vtable_mh.ChangeMethod(mir_method);
             if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
               miranda_method.Assign(mir_method);
@@ -4493,13 +4537,13 @@
           }
           if (miranda_method.Get() == NULL) {
             // Point the interface table at a phantom slot.
-            miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_method->Clone(self)));
+            miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_mh.Get()->Clone(self)));
             if (UNLIKELY(miranda_method.Get() == NULL)) {
               CHECK(self->IsExceptionPending());  // OOME.
               return false;
             }
-            // TODO: If a methods move then the miranda_list may hold stale references.
-            miranda_list.push_back(miranda_method.Get());
+            DCHECK_LT(miranda_list_size, max_miranda_methods);
+            miranda_list->Set<false>(miranda_list_size++, miranda_method.Get());
           }
           method_array->Set<false>(j, miranda_method.Get());
         }
@@ -4516,9 +4560,9 @@
     }
     klass->SetImTable(imtable.Get());
   }
-  if (!miranda_list.empty()) {
+  if (miranda_list_size > 0) {
     int old_method_count = klass->NumVirtualMethods();
-    int new_method_count = old_method_count + miranda_list.size();
+    int new_method_count = old_method_count + miranda_list_size;
     mirror::ObjectArray<mirror::ArtMethod>* virtuals;
     if (old_method_count == 0) {
       virtuals = AllocArtMethodArray(self, new_method_count);
@@ -4536,14 +4580,14 @@
         hs.NewHandle(klass->GetVTableDuringLinking()));
     CHECK(vtable.Get() != NULL);
     int old_vtable_count = vtable->GetLength();
-    int new_vtable_count = old_vtable_count + miranda_list.size();
+    int new_vtable_count = old_vtable_count + miranda_list_size;
     vtable.Assign(vtable->CopyOf(self, new_vtable_count));
     if (UNLIKELY(vtable.Get() == NULL)) {
       CHECK(self->IsExceptionPending());  // OOME.
       return false;
     }
-    for (size_t i = 0; i < miranda_list.size(); ++i) {
-      mirror::ArtMethod* method = miranda_list[i];
+    for (size_t i = 0; i < miranda_list_size; ++i) {
+      mirror::ArtMethod* method = miranda_list->Get(i);
       // Leave the declaring class alone as type indices are relative to it
       method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
       method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9ae3862..5694149 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -573,6 +573,10 @@
                                             std::vector<std::string>* error_msg)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
+  const OatFile* GetInterpretedOnlyOat(const std::string& oat_path,
+                                       InstructionSet isa,
+                                       std::string* error_msg);
+
   const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
                                      const std::string& image_location, InstructionSet isa,
                                      std::string* error_msg)
@@ -752,6 +756,7 @@
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
   friend class NoDex2OatTest;  // for FindOpenedOatFileForDexFile
+  friend class NoPatchoatTest;  // for FindOpenedOatFileForDexFile
   FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
   FRIEND_TEST(mirror::DexCacheTest, Open);
   FRIEND_TEST(ExceptionTest, FindExceptionHandler);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 1cddb8b..6d2f21e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -664,6 +664,11 @@
 }
 
 void Dbg::StopJdwp() {
+  // Post VM_DEATH event before the JDWP connection is closed (either by the JDWP thread or the
+  // destruction of gJdwpState).
+  if (gJdwpState != nullptr && gJdwpState->IsActive()) {
+    gJdwpState->PostVMDeath();
+  }
   // Prevent the JDWP thread from processing JDWP incoming packets after we close the connection.
   Disposed();
   delete gJdwpState;
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index a0e35f8..cf89850 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -211,7 +211,7 @@
   }
 
   bool explicit_overflow_check = Runtime::Current()->ExplicitStackOverflowChecks();
-  self->ResetDefaultStackEnd(!explicit_overflow_check);  // Return to default stack size.
+  self->ResetDefaultStackEnd();  // Return to default stack size.
 
   // And restore protection if implicit checks are on.
   if (!explicit_overflow_check) {
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 217360f..3b06f74 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -55,7 +55,7 @@
   // scan_end is the byte after the last byte we scan.
   DCHECK_LE(scan_end, reinterpret_cast<byte*>(bitmap->HeapLimit()));
   byte* card_cur = CardFromAddr(scan_begin);
-  byte* card_end = CardFromAddr(scan_end);
+  byte* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
   CheckCardValid(card_cur);
   CheckCardValid(card_end);
   size_t cards_scanned = 0;
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2686af0..3acf80d 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -72,9 +72,11 @@
 
 class ModUnionUpdateObjectReferencesVisitor {
  public:
-  ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg)
-    : callback_(callback),
-      arg_(arg) {
+  ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
+                                        space::ContinuousSpace* from_space,
+                                        bool* contains_reference_to_other_space)
+    : callback_(callback), arg_(arg), from_space_(from_space),
+      contains_reference_to_other_space_(contains_reference_to_other_space) {
   }
 
   // Extra parameters are required since we use this same visitor signature for checking objects.
@@ -82,7 +84,9 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Only add the reference if it is non null and fits our criteria.
     mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
-    if (obj_ptr->AsMirrorPtr() != nullptr) {
+    mirror::Object* ref = obj_ptr->AsMirrorPtr();
+    if (ref != nullptr && !from_space_->HasAddress(ref)) {
+      *contains_reference_to_other_space_ = true;
       callback_(obj_ptr, arg_);
     }
   }
@@ -90,24 +94,36 @@
  private:
   MarkHeapReferenceCallback* const callback_;
   void* arg_;
+  // Space which we are scanning
+  space::ContinuousSpace* const from_space_;
+  // Set if we have any references to another space.
+  bool* const contains_reference_to_other_space_;
 };
 
 class ModUnionScanImageRootVisitor {
  public:
-  ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg)
-      : callback_(callback), arg_(arg) {}
+  ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
+                               space::ContinuousSpace* from_space,
+                               bool* contains_reference_to_other_space)
+      : callback_(callback), arg_(arg), from_space_(from_space),
+        contains_reference_to_other_space_(contains_reference_to_other_space) {}
 
   void operator()(Object* root) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(root != NULL);
-    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_);
+    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+                                                      contains_reference_to_other_space_);
     root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
   }
 
  private:
   MarkHeapReferenceCallback* const callback_;
   void* const arg_;
+  // Space which we are scanning
+  space::ContinuousSpace* const from_space_;
+  // Set if we have any references to another space.
+  bool* const contains_reference_to_other_space_;
 };
 
 void ModUnionTableReferenceCache::ClearCards() {
@@ -313,12 +329,20 @@
 void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
                                                      void* arg) {
   CardTable* card_table = heap_->GetCardTable();
-  ModUnionScanImageRootVisitor scan_visitor(callback, arg);
   ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
-  for (const byte* card_addr : cleared_cards_) {
-    uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+  bool reference_to_other_space = false;
+  ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+  for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
+    uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
     DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+    reference_to_other_space = false;
     bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
+    if (!reference_to_other_space) {
+      // No non null reference to another space, remove the card.
+      it = cleared_cards_.erase(it);
+    } else {
+      ++it;
+    }
   }
 }
 
@@ -333,6 +357,17 @@
   os << "]";
 }
 
+void ModUnionTableCardCache::SetCards() {
+  CardTable* card_table = heap_->GetCardTable();
+  for (byte* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
+       addr += CardTable::kCardSize) {
+    cleared_cards_.insert(card_table->CardFromAddr(addr));
+  }
+}
+
+void ModUnionTableReferenceCache::SetCards() {
+}
+
 }  // namespace accounting
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index 449e171..f67dc27 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -65,6 +65,9 @@
   // determining references to track.
   virtual void ClearCards() = 0;
 
+  // Set all the cards.
+  virtual void SetCards() = 0;
+
   // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
   // before a call to update, for example, back-to-back sticky GCs. Also mark references to other
   // spaces which are stored in the mod-union table.
@@ -120,6 +123,8 @@
 
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void SetCards() OVERRIDE;
+
  protected:
   // Cleared card array, used to update the mod-union table.
   ModUnionTable::CardSet cleared_cards_;
@@ -150,6 +155,8 @@
 
   void Dump(std::ostream& os);
 
+  void SetCards() OVERRIDE;
+
  protected:
   // Cleared card array, used to update the mod-union table.
   CardSet cleared_cards_;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 7d3fd2d..d1fb600 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -84,7 +84,9 @@
                                    &klass);
       if (obj == nullptr) {
         bool after_is_current_allocator = allocator == GetCurrentAllocator();
-        if (is_current_allocator && !after_is_current_allocator) {
+        // If there is a pending exception, fail the allocation right away since the next one
+        // could cause OOM and abort the runtime.
+        if (!self->IsExceptionPending() && is_current_allocator && !after_is_current_allocator) {
           // If the allocator changed, we need to restart the allocation.
           return AllocObject<kInstrumented>(self, klass, byte_count, pre_fence_visitor);
         }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5d138d2..f0b7685 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -128,8 +128,8 @@
       long_gc_log_threshold_(long_gc_log_threshold),
       ignore_max_footprint_(ignore_max_footprint),
       zygote_creation_lock_("zygote creation lock", kZygoteCreationLock),
-      have_zygote_space_(false),
-      large_object_threshold_(std::numeric_limits<size_t>::max()),  // Starts out disabled.
+      zygote_space_(nullptr),
+      large_object_threshold_(kDefaultLargeObjectThreshold),  // Starts out disabled.
       collector_type_running_(kCollectorTypeNone),
       last_gc_type_(collector::kGcTypeNone),
       next_gc_type_(collector::kGcTypePartial),
@@ -190,7 +190,6 @@
   // If we aren't the zygote, switch to the default non zygote allocator. This may update the
   // entrypoints.
   if (!Runtime::Current()->IsZygote()) {
-    large_object_threshold_ = kDefaultLargeObjectThreshold;
     // Background compaction is currently not supported for command line runs.
     if (background_collector_type_ != foreground_collector_type_) {
       VLOG(heap) << "Disabling background compaction for non zygote";
@@ -468,7 +467,7 @@
     // After the zygote we want this to be false if we don't have background compaction enabled so
     // that getting primitive array elements is faster.
     // We never have homogeneous compaction with GSS and don't need a space with movable objects.
-    can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS;
+    can_move_objects = !HasZygoteSpace() && foreground_collector_type_ != kCollectorTypeGSS;
   }
   if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
     RemoveRememberedSet(main_space_);
@@ -801,6 +800,9 @@
     os << "Mean allocation time: " << PrettyDuration(allocation_time / total_objects_allocated)
        << "\n";
   }
+  if (HasZygoteSpace()) {
+    os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
+  }
   os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
   os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
   os << "Approximate GC data structures memory overhead: " << gc_memory_overhead_.LoadRelaxed();
@@ -1823,7 +1825,8 @@
   Thread* self = Thread::Current();
   MutexLock mu(self, zygote_creation_lock_);
   // Try to see if we have any Zygote spaces.
-  if (have_zygote_space_) {
+  if (HasZygoteSpace()) {
+    LOG(WARNING) << __FUNCTION__ << " called when we already have a zygote space.";
     return;
   }
   VLOG(heap) << "Starting PreZygoteFork";
@@ -1897,26 +1900,26 @@
     // from this point on.
     RemoveRememberedSet(old_alloc_space);
   }
-  space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
-                                                                        low_memory_mode_,
-                                                                        &non_moving_space_);
+  zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_,
+                                                     &non_moving_space_);
   CHECK(!non_moving_space_->CanMoveObjects());
   if (same_space) {
     main_space_ = non_moving_space_;
     SetSpaceAsDefault(main_space_);
   }
   delete old_alloc_space;
-  CHECK(zygote_space != nullptr) << "Failed creating zygote space";
-  AddSpace(zygote_space);
+  CHECK(HasZygoteSpace()) << "Failed creating zygote space";
+  AddSpace(zygote_space_);
   non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
   AddSpace(non_moving_space_);
-  have_zygote_space_ = true;
-  // Enable large object space allocations.
-  large_object_threshold_ = kDefaultLargeObjectThreshold;
   // Create the zygote space mod union table.
   accounting::ModUnionTable* mod_union_table =
-      new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+      new accounting::ModUnionTableCardCache("zygote space mod-union table", this,
+                                             zygote_space_);
   CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+  // Set all the cards in the mod-union table since we don't know which objects contain references
+  // to large objects.
+  mod_union_table->SetCards();
   AddModUnionTable(mod_union_table);
   if (collector::SemiSpace::kUseRememberedSet) {
     // Add a new remembered set for the post-zygote non-moving space.
@@ -1986,7 +1989,7 @@
   // If the heap can't run the GC, silently fail and return that no GC was run.
   switch (gc_type) {
     case collector::kGcTypePartial: {
-      if (!have_zygote_space_) {
+      if (!HasZygoteSpace()) {
         return collector::kGcTypeNone;
       }
       break;
@@ -2483,7 +2486,6 @@
 bool Heap::VerifyMissingCardMarks() {
   Thread* self = Thread::Current();
   Locks::mutator_lock_->AssertExclusiveHeld(self);
-
   // We need to sort the live stack since we binary search it.
   live_stack_->Sort();
   // Since we sorted the allocation stack content, need to revoke all
@@ -2491,7 +2493,6 @@
   RevokeAllThreadLocalAllocationStacks(self);
   VerifyLiveStackReferences visitor(this);
   GetLiveBitmap()->Visit(visitor);
-
   // We can verify objects in the live stack since none of these should reference dead objects.
   for (mirror::Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
     if (!kUseThreadLocalAllocationStack || *it != nullptr) {
@@ -2689,7 +2690,7 @@
 void Heap::PostGcVerification(collector::GarbageCollector* gc) {
   if (verify_system_weaks_ || verify_post_gc_rosalloc_ || verify_post_gc_heap_) {
     collector::GarbageCollector::ScopedPause pause(gc);
-    PreGcVerificationPaused(gc);
+    PostGcVerificationPaused(gc);
   }
 }
 
@@ -2812,7 +2813,7 @@
     next_gc_type_ = collector::kGcTypeSticky;
   } else {
     collector::GcType non_sticky_gc_type =
-        have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull;
+        HasZygoteSpace() ? collector::kGcTypePartial : collector::kGcTypeFull;
     // Find what the next non sticky collector will be.
     collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
     // If the throughput of the current sticky GC >= throughput of the non sticky collector, then
@@ -3035,7 +3036,7 @@
   size_t new_native_bytes_allocated = native_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes);
   new_native_bytes_allocated += bytes;
   if (new_native_bytes_allocated > native_footprint_gc_watermark_) {
-    collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial :
+    collector::GcType gc_type = HasZygoteSpace() ? collector::kGcTypePartial :
         collector::kGcTypeFull;
 
     // The second watermark is higher than the gc watermark. If you hit this it means you are
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d5b49d8..ed93ad9 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -79,6 +79,7 @@
 namespace space {
   class AllocSpace;
   class BumpPointerSpace;
+  class ContinuousMemMapAllocSpace;
   class DiscontinuousSpace;
   class DlMallocSpace;
   class ImageSpace;
@@ -87,7 +88,7 @@
   class RosAllocSpace;
   class Space;
   class SpaceTest;
-  class ContinuousMemMapAllocSpace;
+  class ZygoteSpace;
 }  // namespace space
 
 class AgeCardVisitor {
@@ -599,6 +600,10 @@
     return &reference_processor_;
   }
 
+  bool HasZygoteSpace() const {
+    return zygote_space_ != nullptr;
+  }
+
  private:
   // Compact source space to target space.
   void Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -849,8 +854,9 @@
   // Lock which guards zygote space creation.
   Mutex zygote_creation_lock_;
 
-  // If we have a zygote space.
-  bool have_zygote_space_;
+  // Non-null iff we have a zygote space. Doesn't contain the large objects allocated before
+  // zygote space creation.
+  space::ZygoteSpace* zygote_space_;
 
   // Minimum allocation size of large object.
   size_t large_object_threshold_;
diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc
index d8a38f4..644e055 100644
--- a/runtime/instruction_set.cc
+++ b/runtime/instruction_set.cc
@@ -42,21 +42,18 @@
 InstructionSet GetInstructionSetFromString(const char* isa_str) {
   CHECK(isa_str != nullptr);
 
-  if (!strcmp("arm", isa_str)) {
+  if (strcmp("arm", isa_str) == 0) {
     return kArm;
-  } else if (!strcmp("arm64", isa_str)) {
+  } else if (strcmp("arm64", isa_str) == 0) {
     return kArm64;
-  } else if (!strcmp("x86", isa_str)) {
+  } else if (strcmp("x86", isa_str) == 0) {
     return kX86;
-  } else if (!strcmp("x86_64", isa_str)) {
+  } else if (strcmp("x86_64", isa_str) == 0) {
     return kX86_64;
-  } else if (!strcmp("mips", isa_str)) {
+  } else if (strcmp("mips", isa_str) == 0) {
     return kMips;
-  } else if (!strcmp("none", isa_str)) {
-    return kNone;
   }
 
-  LOG(FATAL) << "Unknown ISA " << isa_str;
   return kNone;
 }
 
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index f212811..ae8eeac 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -75,6 +75,8 @@
 
 
 const char* GetInstructionSetString(InstructionSet isa);
+
+// Note: Returns kNone when the string cannot be parsed to a known value.
 InstructionSet GetInstructionSetFromString(const char* instruction_set);
 
 static inline size_t GetInstructionSetPointerSize(InstructionSet isa) {
diff --git a/runtime/instruction_set_test.cc b/runtime/instruction_set_test.cc
index ece3238..ac17c4f 100644
--- a/runtime/instruction_set_test.cc
+++ b/runtime/instruction_set_test.cc
@@ -29,6 +29,7 @@
   EXPECT_EQ(kX86_64, GetInstructionSetFromString("x86_64"));
   EXPECT_EQ(kMips, GetInstructionSetFromString("mips"));
   EXPECT_EQ(kNone, GetInstructionSetFromString("none"));
+  EXPECT_EQ(kNone, GetInstructionSetFromString("random-string"));
 }
 
 TEST_F(InstructionSetTest, GetInstructionSetString) {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d5c6e4f..0f45b9e 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -121,11 +121,9 @@
     // Do not change stubs for these methods.
     return;
   }
-  std::string temp;
-  // Note that the Proxy class itself is not a proxy class.
-  if (strcmp(method->GetDeclaringClass()->GetDescriptor(&temp), "Ljava/lang/reflect/Proxy;") == 0 &&
-      method->IsConstructor()) {
-    // Do not stub Proxy.<init>.
+  // Don't stub Proxy.<init>. Note that the Proxy class itself is not a proxy class.
+  if (method->IsConstructor() &&
+      method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) {
     return;
   }
   const void* new_portable_code;
@@ -497,7 +495,7 @@
     }
   }
   if ((events & kFieldRead) != 0) {
-    if (have_dex_pc_listeners_) {
+    if (have_field_read_listeners_) {
       std::list<InstrumentationListener*>* modified =
           new std::list<InstrumentationListener*>(*field_read_listeners_.get());
       modified->remove(listener);
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 7795b7c..4155c82 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -283,7 +283,9 @@
     {
       ScopedThreadStateChange tsc(self, kWaitingForDebuggerToAttach);
       MutexLock attach_locker(self, state->attach_lock_);
-      state->attach_cond_.Wait(self);
+      while (state->debug_thread_id_ == 0) {
+        state->attach_cond_.Wait(self);
+      }
     }
     if (!state->IsActive()) {
       LOG(ERROR) << "JDWP connection failed";
@@ -335,10 +337,6 @@
  */
 JdwpState::~JdwpState() {
   if (netState != NULL) {
-    if (IsConnected()) {
-      PostVMDeath();
-    }
-
     /*
      * Close down the network to inspire the thread to halt.
      */
@@ -458,6 +456,7 @@
       if (!netState->Establish(options_)) {
         /* wake anybody who was waiting for us to succeed */
         MutexLock mu(thread_, attach_lock_);
+        debug_thread_id_ = static_cast<ObjectId>(-1);
         attach_cond_.Broadcast(thread_);
         break;
       }
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 844d14a..b236ede 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -746,6 +746,21 @@
   GetMethodIdBadArgumentTest(true);
 }
 
+TEST_F(JniInternalTest, CallVoidMethodNullReceiver) {
+  jclass jlobject = env_->FindClass("java/lang/Object");
+  jmethodID method;
+
+  // Check that GetMethodID for java.lang.NoSuchMethodError.<init>(String) finds the constructor.
+  method = env_->GetMethodID(jlobject, "<init>", "()V");
+  EXPECT_NE(nullptr, method);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  // Null object to CallVoidMethod.
+  CheckJniAbortCatcher check_jni_abort_catcher;
+  env_->CallVoidMethod(nullptr, method);
+  check_jni_abort_catcher.Check("null");
+}
+
 TEST_F(JniInternalTest, GetStaticMethodID) {
   jclass jlobject = env_->FindClass("java/lang/Object");
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
diff --git a/runtime/method_helper.h b/runtime/method_helper.h
index f71d273..8150456 100644
--- a/runtime/method_helper.h
+++ b/runtime/method_helper.h
@@ -41,6 +41,11 @@
     return method_->GetInterfaceMethodIfProxy();
   }
 
+  // GetMethod() != Get() for proxy methods.
+  mirror::ArtMethod* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return method_.Get();
+  }
+
   mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index f199c99..14d6cd9 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -27,6 +27,7 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
+#include "base/stringprintf.h"
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
@@ -490,6 +491,12 @@
   }
 
   const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
+  if (target_instruction_set == kNone) {
+    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
+    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
+    env->ThrowNew(iae.get(), message.c_str());
+    return 0;
+  }
 
   // Get the filename for odex file next to the dex file.
   std::string odex_filename(DexFilenameToOdexFilename(filename, target_instruction_set));
@@ -551,8 +558,16 @@
 static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
     jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
   ScopedUtfChars filename(env, javaFilename);
+  if (env->ExceptionCheck()) {
+    return 0;
+  }
+
   NullableScopedUtfChars pkgname(env, javaPkgname);
+
   ScopedUtfChars instruction_set(env, javaInstructionSet);
+  if (env->ExceptionCheck()) {
+    return 0;
+  }
 
   return IsDexOptNeededInternal(env, filename.c_str(), pkgname.c_str(),
                                 instruction_set.c_str(), defer);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b74b10f..50dfe21 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -45,7 +45,7 @@
                              std::string* error_msg) {
   CHECK(!oat_contents.empty()) << location;
   CheckLocation(location);
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
   oat_file->begin_ = &oat_contents[0];
   oat_file->end_ = &oat_contents[oat_contents.size()];
   return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
@@ -97,7 +97,7 @@
                              const std::string& location,
                              byte* requested_base,
                              std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, true));
   bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg);
   if (!success) {
     return nullptr;
@@ -111,7 +111,7 @@
                               bool writable,
                               bool executable,
                               std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(new OatFile(location));
+  std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
   bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
@@ -120,8 +120,9 @@
   return oat_file.release();
 }
 
-OatFile::OatFile(const std::string& location)
-    : location_(location), begin_(NULL), end_(NULL), dlopen_handle_(NULL),
+OatFile::OatFile(const std::string& location, bool is_executable)
+    : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable),
+      dlopen_handle_(NULL),
       secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) {
   CHECK(!location_.empty());
 }
@@ -533,10 +534,17 @@
     methods_pointer_index = num_set_bits;
   }
   const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index];
-  return OatMethod(
-      oat_file_->Begin(),
-      oat_method_offsets.code_offset_,
-      oat_method_offsets.gc_map_offset_);
+  if (oat_file_->IsExecutable()
+      || (Runtime::Current() == nullptr)
+      || Runtime::Current()->IsCompiler()) {
+    return OatMethod(
+        oat_file_->Begin(),
+        oat_method_offsets.code_offset_,
+        oat_method_offsets.gc_map_offset_);
+  } else {
+    // We aren't allowed to use the compiled code. We just force it down the interpreted version.
+    return OatMethod(oat_file_->Begin(), 0, 0);
+  }
 }
 
 
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 508bfc2..8535bf4 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -64,6 +64,10 @@
 
   ~OatFile();
 
+  bool IsExecutable() const {
+    return is_executable_;
+  }
+
   ElfFile* GetElfFile() const {
     CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
         << "Cannot get an elf file from " << GetLocation();
@@ -270,7 +274,7 @@
                               bool executable,
                               std::string* error_msg);
 
-  explicit OatFile(const std::string& filename);
+  explicit OatFile(const std::string& filename, bool executable);
   bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
   bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
                    std::string* error_msg);
@@ -287,6 +291,9 @@
   // Pointer to end of oat region for bounds checking.
   const byte* end_;
 
+  // Was this oat_file loaded executable?
+  const bool is_executable_;
+
   // Backing memory map for oat file during when opened by ElfWriter during initial compilation.
   std::unique_ptr<MemMap> mem_map_;
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index a023ff1..26360d7 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -414,8 +414,12 @@
       compiler_callbacks_ =
           reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
     } else if (option == "imageinstructionset") {
-      image_isa_ = GetInstructionSetFromString(
-          reinterpret_cast<const char*>(options[i].second));
+      const char* isa_str = reinterpret_cast<const char*>(options[i].second);
+      image_isa_ = GetInstructionSetFromString(isa_str);
+      if (image_isa_ == kNone) {
+        Usage("%s is not a valid instruction set.", isa_str);
+        return false;
+      }
     } else if (option == "-Xzygote") {
       is_zygote_ = true;
     } else if (StartsWith(option, "-Xpatchoat:")) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index dcc897f..7ac685b 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -224,7 +224,8 @@
   } else {
     // If we are going to use implicit stack checks, allocate space for the protected
     // region at the bottom of the stack.
-    stack_size += Thread::kStackOverflowImplicitCheckSize;
+    stack_size += Thread::kStackOverflowImplicitCheckSize +
+        GetStackOverflowReservedBytes(kRuntimeISA);
   }
 
   // Some systems require the stack size to be a multiple of the system page size, so round up.
@@ -264,7 +265,7 @@
   // a segv.
 
   // Read every page from the high address to the low.
-  for (byte* p = stack_top; p > pregion; p -= kPageSize) {
+  for (byte* p = stack_top; p >= pregion; p -= kPageSize) {
     dont_optimize_this = *p;
   }
 
@@ -539,7 +540,7 @@
 
   // Set stack_end_ to the bottom of the stack saving space of stack overflows
   bool implicit_stack_check = !Runtime::Current()->ExplicitStackOverflowChecks();
-  ResetDefaultStackEnd(implicit_stack_check);
+  ResetDefaultStackEnd();
 
   // Install the protected region if we are doing implicit overflow checks.
   if (implicit_stack_check) {
@@ -551,8 +552,19 @@
     // The thread might have protected region at the bottom.  We need
     // to install our own region so we need to move the limits
     // of the stack to make room for it.
-    tlsPtr_.stack_begin += guardsize;
-    tlsPtr_.stack_end += guardsize;
+
+#if defined(__i386__) || defined(__x86_64__)
+    // Work around issue trying to read last page of stack on Intel.
+    // The bug for this is b/17111575.  The problem is that we are
+    // unable to read the page just above the guard page on the
+    // main stack on an intel target.  When the bug is fixed
+    // this can be removed.
+    if (::art::GetTid() == getpid()) {
+      guardsize += 4 * KB;
+    }
+#endif
+    tlsPtr_.stack_begin += guardsize + kStackOverflowProtectedSize;
+    tlsPtr_.stack_end += guardsize + kStackOverflowProtectedSize;
     tlsPtr_.stack_size -= guardsize;
 
     InstallImplicitProtection();
diff --git a/runtime/thread.h b/runtime/thread.h
index 120ff6f..fe950c4 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -594,16 +594,10 @@
   void SetStackEndForStackOverflow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Set the stack end to that to be used during regular execution
-  void ResetDefaultStackEnd(bool implicit_overflow_check) {
+  void ResetDefaultStackEnd() {
     // Our stacks grow down, so we want stack_end_ to be near there, but reserving enough room
     // to throw a StackOverflowError.
-    if (implicit_overflow_check) {
-      // For implicit checks we also need to add in the protected region above the
-      // overflow region.
-      tlsPtr_.stack_end = tlsPtr_.stack_begin + kStackOverflowImplicitCheckSize;
-    } else {
-      tlsPtr_.stack_end = tlsPtr_.stack_begin + GetStackOverflowReservedBytes(kRuntimeISA);
-    }
+    tlsPtr_.stack_end = tlsPtr_.stack_begin + GetStackOverflowReservedBytes(kRuntimeISA);
   }
 
   // Install the protected region for implicit stack checks.
diff --git a/runtime/utils.cc b/runtime/utils.cc
index b55977b..d7e215b 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -66,8 +66,9 @@
   uint64_t owner;
   CHECK_PTHREAD_CALL(pthread_threadid_np, (NULL, &owner), __FUNCTION__);  // Requires Mac OS 10.6
   return owner;
+#elif defined(__BIONIC__)
+  return gettid();
 #else
-  // Neither bionic nor glibc exposes gettid(2).
   return syscall(__NR_gettid);
 #endif
 }
@@ -999,7 +1000,7 @@
   } else {
     s = thread_name + len - 15;
   }
-#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+#if defined(__BIONIC__)
   // pthread_setname_np fails rather than truncating long strings.
   char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
   strncpy(buf, s, sizeof(buf)-1);
diff --git a/test/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt
new file mode 100644
index 0000000..a1293ae
--- /dev/null
+++ b/test/117-nopatchoat/expected.txt
@@ -0,0 +1,9 @@
+Run without dex2oat/patchoat
+dex2oat & patchoat are disabled, has oat is true, has executable oat is false.
+This is a function call
+Run with dexoat/patchoat
+dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+This is a function call
+Run default
+dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+This is a function call
diff --git a/test/117-nopatchoat/info.txt b/test/117-nopatchoat/info.txt
new file mode 100644
index 0000000..aa9f57c
--- /dev/null
+++ b/test/117-nopatchoat/info.txt
@@ -0,0 +1 @@
+Test that disables patchoat'ing the application.
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
new file mode 100644
index 0000000..ced7f6e
--- /dev/null
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#include "class_linker.h"
+#include "dex_file-inl.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+class NoPatchoatTest {
+ public:
+  static bool hasExecutableOat(jclass cls) {
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+    const DexFile& dex_file = klass->GetDexFile();
+    const OatFile* oat_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
+    return oat_file != nullptr && oat_file->IsExecutable();
+  }
+};
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) {
+  return NoPatchoatTest::hasExecutableOat(cls);
+}
+
+}  // namespace art
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
new file mode 100755
index 0000000..a7c96a0
--- /dev/null
+++ b/test/117-nopatchoat/run
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# 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.
+
+# ensure flags includes prebuild and relocate. It doesn't make sense unless we
+# have a oat file we want to relocate.
+# TODO Unfortunately we have no way to force prebuild on for both host and target (or skip if not on).
+flags="${@/--relocate/}"
+flags="${flags/--no-relocate/}"
+flags="${flags} --relocate"
+
+# Make sure we can run without relocation
+echo "Run without dex2oat/patchoat"
+# /bin/false is actually not even there for either, so the exec will fail.
+# Unfortunately there is no equivalent to /bin/false in android.
+${RUN} ${flags} --runtime-option -Xnodex2oat
+
+# Make sure we can run with the oat file.
+echo "Run with dexoat/patchoat"
+${RUN} ${flags} --runtime-option -Xdex2oat
+
+# Make sure we can run with the default settings.
+echo "Run default"
+${RUN} ${flags}
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
new file mode 100644
index 0000000..f3f91ce
--- /dev/null
+++ b/test/117-nopatchoat/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println(
+        "dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
+        ", has oat is " + hasOat() + ", has executable oat is " + hasExecutableOat() + ".");
+
+    if (!hasOat() && isDex2OatEnabled()) {
+      throw new Error("Application with dex2oat enabled runs without an oat file");
+    }
+
+    System.out.println(functionCall());
+  }
+
+  public static String functionCall() {
+    String arr[] = {"This", "is", "a", "function", "call"};
+    String ret = "";
+    for (int i = 0; i < arr.length; i++) {
+      ret = ret + arr[i] + " ";
+    }
+    return ret.substring(0, ret.length() - 1);
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  private native static boolean isDex2OatEnabled();
+
+  private native static boolean hasOat();
+
+  private native static boolean hasExecutableOat();
+}
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
new file mode 100644
index 0000000..eacf730
--- /dev/null
+++ b/test/702-LargeBranchOffset/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of source files.
+cpp -P src/Main.java.in src/Main.java
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+${DX} --debug --dex --output=classes.dex classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/702-LargeBranchOffset/expected.txt b/test/702-LargeBranchOffset/expected.txt
new file mode 100644
index 0000000..130678f
--- /dev/null
+++ b/test/702-LargeBranchOffset/expected.txt
@@ -0,0 +1,5 @@
+0
+0
+2
+1
+512
diff --git a/test/702-LargeBranchOffset/info.txt b/test/702-LargeBranchOffset/info.txt
new file mode 100644
index 0000000..747263e
--- /dev/null
+++ b/test/702-LargeBranchOffset/info.txt
@@ -0,0 +1 @@
+Simple test to check if large branch offset works correctly.
diff --git a/test/702-LargeBranchOffset/src/Main.java.in b/test/702-LargeBranchOffset/src/Main.java.in
new file mode 100644
index 0000000..270d766
--- /dev/null
+++ b/test/702-LargeBranchOffset/src/Main.java.in
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#define DO_2_TIMES(x) x x
+#define DO_4_TIMES(x) DO_2_TIMES(DO_2_TIMES(x))
+#define DO_16_TIMES(x) DO_4_TIMES(DO_4_TIMES(x))
+#define DO_256_TIMES(x) DO_16_TIMES(DO_16_TIMES(x))
+#define DO_512_TIMES(x) DO_256_TIMES(DO_2_TIMES(x))
+
+
+public class Main {
+  public static void main(String[] args) {
+    Main m = new Main();
+    System.out.println(m.foo(-1, -1));
+    System.out.println(m.foo(-1, +1));
+    System.out.println(m.foo(+1, -1));
+    System.out.println(m.foo(+1, +1));
+    System.out.println(m.value);
+  }
+
+  public int foo(int a, int b) {
+    if ( a >= 0 ) {
+      if ( b < 0 ) {
+        DO_512_TIMES( synchronized(lock) { value++; } )
+        return 2;
+      }
+      return 1;
+    }
+    return 0;
+  }
+
+  Object lock = new Object();
+  int value = 0;
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 3871b28..caaf649 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,7 +24,8 @@
   004-ReferenceMap/stack_walk_refmap_jni.cc \
   004-StackWalk/stack_walk_jni.cc \
   004-UnsafeTest/unsafe_test.cc \
-  116-nodex2oat/nodex2oat.cc
+  116-nodex2oat/nodex2oat.cc \
+  117-nopatchoat/nopatchoat.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 0cf9e16..ce0eb3f 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -124,6 +124,42 @@
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-no-prebuild)
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-no-prebuild)
 
+# NB 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
+# On host this is patched around by changing a run flag but we cannot do this on the target due to
+# a different run-script.
+TEST_ART_TARGET_BROKEN_PREBUILD_RUN_TESTS := \
+  116-nodex2oat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-trace,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcverify,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcstress,-prebuild))
+
+# NB 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without --prebuild --relocate
+TEST_ART_BROKEN_RELOCATE_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+
+TEST_ART_BROKEN_NORELOCATE_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
+  117-nopatchoat
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
+
 # The path where build only targets will be output, e.g.
 # out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
 art_run_tests_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA
diff --git a/test/run-test b/test/run-test
index b8e40d9..6fac03b 100755
--- a/test/run-test
+++ b/test/run-test
@@ -212,6 +212,7 @@
 # Cannot use realpath, as it does not exist on Mac.
 # Cannot us a simple "cd", as the path might not be created yet.
 # Use -m option of readlink: canonicalizes, but allows non-existing components.
+noncanonical_tmp_dir=$tmp_dir
 tmp_dir="`cd $oldwd ; readlink -m $tmp_dir`"
 
 mkdir -p $tmp_dir
@@ -474,6 +475,8 @@
         "./${run}" $run_args "$@" >"$output" 2>&1
     else
         cp "$build_output" "$output"
+        echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`"
+        echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}"
         echo "build exit status: $build_exit" >>"$output"
     fi
     ./$check_cmd "$expected" "$output"