Merge "ART: Fix unchecked register index validity"
diff --git a/Android.mk b/Android.mk
index 15e8308..669939b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -355,7 +355,7 @@
 build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_GTEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
 
 ########################################################################
-# "m art-host" for just building the files needed to run the art script
+# "m art-host" for just building the files needed to run the art script on the host.
 .PHONY: art-host
 ifeq ($(HOST_PREFER_32_BIT),true)
 art-host:   $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/bin/dalvikvm
@@ -363,6 +363,11 @@
 art-host:   $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm64 $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/lib64/libjavacore.so $(HOST_OUT)/bin/dalvikvm
 endif
 
+# "m art-target" for just building the files needed to run the art script on the target.
+# Note that the script can run a dalvikvm executable that is not necessarily /system/bin/dalvikvm.
+.PHONY: art-target
+art-target: adb build-art-target art libnativehelper libjavacore
+
 .PHONY: art-host-debug
 art-host-debug:   art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd $(HOST_OUT)/bin/patchoatd
 
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f9a78be..ba5bd30 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -164,6 +164,25 @@
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
+                               const std::vector<uint8_t>& quick_code,
+                               const size_t frame_size_in_bytes,
+                               const uint32_t core_spill_mask,
+                               const uint32_t fp_spill_mask,
+                               const std::vector<uint8_t>& mapping_table,
+                               const std::vector<uint8_t>& stack_map)
+    : CompiledCode(driver, instruction_set, quick_code),
+      frame_size_in_bytes_(frame_size_in_bytes),
+      core_spill_mask_(core_spill_mask),
+      fp_spill_mask_(fp_spill_mask),
+      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      vmap_table_(driver->DeduplicateVMapTable(stack_map)),
+      gc_map_(nullptr),
+      cfi_info_(nullptr) {
+}
+
+CompiledMethod::CompiledMethod(CompilerDriver* driver,
+                               InstructionSet instruction_set,
                                const std::vector<uint8_t>& code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 36f4745..3e34144 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -173,7 +173,7 @@
 
 class CompiledMethod FINAL : public CompiledCode {
  public:
-  // Constructs a CompiledMethod for the non-LLVM compilers.
+  // Constructs a CompiledMethod for Quick.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
                  const std::vector<uint8_t>& quick_code,
@@ -186,6 +186,16 @@
                  const std::vector<uint8_t>& native_gc_map,
                  const std::vector<uint8_t>* cfi_info);
 
+  // Constructs a CompiledMethod for Optimizing.
+  CompiledMethod(CompilerDriver* driver,
+                 InstructionSet instruction_set,
+                 const std::vector<uint8_t>& quick_code,
+                 const size_t frame_size_in_bytes,
+                 const uint32_t core_spill_mask,
+                 const uint32_t fp_spill_mask,
+                 const std::vector<uint8_t>& mapping_table,
+                 const std::vector<uint8_t>& vmap_table);
+
   // Constructs a CompiledMethod for the QuickJniCompiler.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
@@ -232,9 +242,8 @@
     return *vmap_table_;
   }
 
-  const std::vector<uint8_t>& GetGcMap() const {
-    DCHECK(gc_map_ != nullptr);
-    return *gc_map_;
+  std::vector<uint8_t> const* GetGcMap() const {
+    return gc_map_;
   }
 
   const std::vector<uint8_t>* GetCFIInfo() const {
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..b95789e 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -556,22 +556,24 @@
 
 // Instruction assembly field_loc kind.
 enum ArmEncodingKind {
-  kFmtUnused,    // Unused field and marks end of formats.
-  kFmtBitBlt,    // Bit string using end/start.
-  kFmtDfp,       // Double FP reg.
-  kFmtSfp,       // Single FP reg.
-  kFmtModImm,    // Shifted 8-bit immed using [26,14..12,7..0].
-  kFmtImm16,     // Zero-extended immed using [26,19..16,14..12,7..0].
-  kFmtImm6,      // Encoded branch target using [9,7..3]0.
-  kFmtImm12,     // Zero-extended immediate using [26,14..12,7..0].
-  kFmtShift,     // Shift descriptor, [14..12,7..4].
-  kFmtLsb,       // least significant bit using [14..12][7..6].
-  kFmtBWidth,    // bit-field width, encoded as width-1.
-  kFmtShift5,    // Shift count, [14..12,7..6].
-  kFmtBrOffset,  // Signed extended [26,11,13,21-16,10-0]:0.
-  kFmtFPImm,     // Encoded floating point immediate.
-  kFmtOff24,     // 24-bit Thumb2 unconditional branch encoding.
-  kFmtSkip,      // Unused field, but continue to next.
+  kFmtUnused,      // Unused field and marks end of formats.
+  kFmtBitBlt,      // Bit string using end/start.
+  kFmtLdmRegList,  // Load multiple register list using [15,14,12..0].
+  kFmtStmRegList,  // Store multiple register list using [14,12..0].
+  kFmtDfp,         // Double FP reg.
+  kFmtSfp,         // Single FP reg.
+  kFmtModImm,      // Shifted 8-bit immed using [26,14..12,7..0].
+  kFmtImm16,       // Zero-extended immed using [26,19..16,14..12,7..0].
+  kFmtImm6,        // Encoded branch target using [9,7..3]0.
+  kFmtImm12,       // Zero-extended immediate using [26,14..12,7..0].
+  kFmtShift,       // Shift descriptor, [14..12,7..4].
+  kFmtLsb,         // least significant bit using [14..12][7..6].
+  kFmtBWidth,      // bit-field width, encoded as width-1.
+  kFmtShift5,      // Shift count, [14..12,7..6].
+  kFmtBrOffset,    // Signed extended [26,11,13,21-16,10-0]:0.
+  kFmtFPImm,       // Encoded floating point immediate.
+  kFmtOff24,       // 24-bit Thumb2 unconditional branch encoding.
+  kFmtSkip,        // Unused field, but continue to next.
 };
 
 // Struct used to define the snippet positions for each Thumb opcode.
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..06d9dd5 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -560,12 +560,12 @@
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
                  "vmov.f64 ", " !0S, !1S", 4, kFixupNone),
     ENCODING_MAP(kThumb2Ldmia,         0xe8900000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
                  "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
     ENCODING_MAP(kThumb2Stmia,         0xe8800000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
                  "stmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -935,7 +935,7 @@
                  IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD_OFF,
                  "ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
     ENCODING_MAP(kThumb2Stm,          0xe9000000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
                  "stm", "!0C, <!1R>", 4, kFixupNone),
@@ -992,7 +992,7 @@
                  kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
                  "movt", "!0C, #!1M", 4, kFixupMovImmHST),
     ENCODING_MAP(kThumb2LdmiaWB,         0xe8b00000,
-                 kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+                 kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
                  kFmtUnused, -1, -1,
                  IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
                  "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -1094,6 +1094,19 @@
             bits |= value;
           } else {
             switch (encoder->field_loc[i].kind) {
+              case kFmtLdmRegList:
+                value = (operand << encoder->field_loc[i].start) &
+                    ((1 << (encoder->field_loc[i].end + 1)) - 1);
+                bits |= value;
+                DCHECK_EQ((bits & (1 << 13)), 0u);
+                break;
+              case kFmtStmRegList:
+                value = (operand << encoder->field_loc[i].start) &
+                    ((1 << (encoder->field_loc[i].end + 1)) - 1);
+                bits |= value;
+                DCHECK_EQ((bits & (1 << 13)), 0u);
+                DCHECK_EQ((bits & (1 << 15)), 0u);
+                break;
               case kFmtSkip:
                 break;  // Nothing to do, but continue to next.
               case kFmtUnused:
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e1d3241..78f5c73 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1107,9 +1107,12 @@
 RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
   RegLocation res;
   if (info->result.location == kLocInvalid) {
-    res = GetReturn(LocToRegClass(info->result));
+    // If result is unused, return a sink target based on type of invoke target.
+    res = GetReturn(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   } else {
     res = info->result;
+    DCHECK_EQ(LocToRegClass(res),
+              ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   }
   return res;
 }
@@ -1117,9 +1120,12 @@
 RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) {
   RegLocation res;
   if (info->result.location == kLocInvalid) {
-    res = GetReturnWide(kCoreReg);
+    // If result is unused, return a sink target based on type of invoke target.
+    res = GetReturnWide(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   } else {
     res = info->result;
+    DCHECK_EQ(LocToRegClass(res),
+              ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
   }
   return res;
 }
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index ef2d9a6..3ca85bf 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -692,33 +692,27 @@
     Clobber(rs_r2);
     LockTemp(rs_r2);
 
-    // Assume that the result will be in EDX.
-    rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r2, INVALID_SREG, INVALID_SREG};
+    // Assume that the result will be in EDX for divide, and EAX for remainder.
+    rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, is_div ? rs_r2 : rs_r0,
+                 INVALID_SREG, INVALID_SREG};
 
-    // Numerator into EAX.
-    RegStorage numerator_reg;
-    if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
-      // We will need the value later.
-      rl_src = LoadValue(rl_src, kCoreReg);
-      numerator_reg = rl_src.reg;
-      OpRegCopy(rs_r0, numerator_reg);
-    } else {
-      // Only need this once.  Just put it into EAX.
-      LoadValueDirectFixed(rl_src, rs_r0);
-    }
+    // We need the value at least twice.  Load into a temp.
+    rl_src = LoadValue(rl_src, kCoreReg);
+    RegStorage numerator_reg = rl_src.reg;
 
-    // Check if numerator is 0
-    OpRegImm(kOpCmp, rs_r0, 0);
+    // Check if numerator is 0.
+    OpRegImm(kOpCmp, numerator_reg, 0);
     LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
-    LoadConstantNoClobber(rs_r2, 0);
+    // Return result 0 if numerator was 0.
+    LoadConstantNoClobber(rl_result.reg, 0);
     LIR* done = NewLIR1(kX86Jmp8, 0);
     branch->target = NewLIR0(kPseudoTargetLabel);
 
-    // EDX = magic.
-    LoadConstantNoClobber(rs_r2, magic);
+    // EAX = magic.
+    LoadConstant(rs_r0, magic);
 
-    // EDX:EAX = magic & dividend.
-    NewLIR1(kX86Imul32DaR, rs_r2.GetReg());
+    // EDX:EAX = magic * numerator.
+    NewLIR1(kX86Imul32DaR, numerator_reg.GetReg());
 
     if (imm > 0 && magic < 0) {
       // Add numerator to EDX.
@@ -756,11 +750,10 @@
       // EAX = numerator * imm.
       OpRegRegImm(kOpMul, rs_r2, rs_r2, imm);
 
-      // EDX -= EAX.
+      // EAX -= EDX.
       NewLIR2(kX86Sub32RR, rs_r0.GetReg(), rs_r2.GetReg());
 
       // For this case, return the result in EAX.
-      rl_result.reg.SetReg(r0);
     }
     done->target = NewLIR0(kPseudoTargetLabel);
   }
@@ -2045,7 +2038,8 @@
     Clobber(rs_r2q);
     LockTemp(rs_r2q);
 
-    RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_r2q, INVALID_SREG, INVALID_SREG};
+    RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
+                             is_div ? rs_r2q : rs_r0q, INVALID_SREG, INVALID_SREG};
 
     // Use H.S.Warren's Hacker's Delight Chapter 10 and
     // T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication.
@@ -2069,24 +2063,35 @@
      * 5. Thus, RDX is the quotient
      */
 
-    // Numerator into RAX.
+    // RAX = magic.
+    LoadConstantWide(rs_r0q, magic);
+
+    // Multiply by numerator.
     RegStorage numerator_reg;
     if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
       // We will need the value later.
       rl_src = LoadValueWide(rl_src, kCoreReg);
       numerator_reg = rl_src.reg;
-      OpRegCopyWide(rs_r0q, numerator_reg);
+
+      // RDX:RAX = magic * numerator.
+      NewLIR1(kX86Imul64DaR, numerator_reg.GetReg());
     } else {
-      // Only need this once.  Just put it into RAX.
-      LoadValueDirectWideFixed(rl_src, rs_r0q);
+      // Only need this once.  Multiply directly from the value.
+      rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+      if (rl_src.location != kLocPhysReg) {
+        // Okay, we can do this from memory.
+        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+        int displacement = SRegOffset(rl_src.s_reg_low);
+        // RDX:RAX = magic * numerator.
+        LIR *m = NewLIR2(kX86Imul64DaM, rs_rX86_SP.GetReg(), displacement);
+        AnnotateDalvikRegAccess(m, displacement >> 2,
+                                true /* is_load */, true /* is_64bit */);
+      } else {
+        // RDX:RAX = magic * numerator.
+        NewLIR1(kX86Imul64DaR, rl_src.reg.GetReg());
+      }
     }
 
-    // RDX = magic.
-    LoadConstantWide(rs_r2q, magic);
-
-    // RDX:RAX = magic & dividend.
-    NewLIR1(kX86Imul64DaR, rs_r2q.GetReg());
-
     if (imm > 0 && magic < 0) {
       // Add numerator to RDX.
       DCHECK(numerator_reg.Valid());
@@ -2134,14 +2139,12 @@
         NewLIR3(kX86Imul64RRI, rs_r2q.GetReg(), rs_r2q.GetReg(), short_imm);
       }
 
-      // RDX -= RAX.
+      // RAX -= RDX.
       OpRegReg(kOpSub, rs_r0q, rs_r2q);
 
-      // Store result.
-      OpRegCopyWide(rl_result.reg, rs_r0q);
+      // Result in RAX.
     } else {
-      // Store result.
-      OpRegCopyWide(rl_result.reg, rs_r2q);
+      // Result in RDX.
     }
     StoreValueWide(rl_dest, rl_result);
     FreeTemp(rs_r0q);
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 7bf7a65..a2b9bb7 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -276,11 +276,11 @@
 
   if (bb->dominators == NULL) {
     bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                 false /* expandable */, kBitMapDominators);
+                                                 true /* expandable */, kBitMapDominators);
     bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                  false /* expandable */, kBitMapIDominated);
+                                                  true /* expandable */, kBitMapIDominated);
     bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks,
-                                                   false /* expandable */, kBitMapDomFrontier);
+                                                   true /* expandable */, kBitMapDomFrontier);
   } else {
     bb->dominators->ClearAllBits();
     bb->i_dominated->ClearAllBits();
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
new file mode 100644
index 0000000..35320f5
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_ELF_BUILDER_H_
+#define ART_COMPILER_ELF_BUILDER_H_
+
+#include "buffered_output_stream.h"
+#include "elf_utils.h"
+#include "file_output_stream.h"
+#include "instruction_set.h"
+
+namespace art {
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfSectionBuilder {
+ public:
+  ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+                    const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link, Elf_Word info,
+                    Elf_Word align, Elf_Word entsize) : name_(sec_name), link_(link) {
+    memset(&section_, 0, sizeof(section_));
+    section_.sh_type = type;
+    section_.sh_flags = flags;
+    section_.sh_info = info;
+    section_.sh_addralign = align;
+    section_.sh_entsize = entsize;
+  }
+
+  virtual ~ElfSectionBuilder() {}
+
+  Elf_Shdr section_;
+  Elf_Word section_index_ = 0;
+
+  Elf_Word GetLink() {
+    return (link_) ? link_->section_index_ : 0;
+  }
+
+  const std::string name_;
+
+ protected:
+  const ElfSectionBuilder* link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
+    if (tag == DT_NULL) {
+      return;
+    }
+    dynamics_.push_back({nullptr, tag, d_un});
+  }
+
+  void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
+                     ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section) {
+    if (tag == DT_NULL) {
+      return;
+    }
+    dynamics_.push_back({section, tag, d_un});
+  }
+
+  ElfDynamicBuilder(const std::string& sec_name,
+                    ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link)
+  : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
+                                                     link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+  ~ElfDynamicBuilder() {}
+
+  Elf_Word GetSize() {
+    // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
+    // these must be added when we actually put the file together because
+    // their values are very dependent on state.
+    return dynamics_.size() + 3;
+  }
+
+  // Create the actual dynamic vector. strsz should be the size of the .dynstr
+  // table and soname_off should be the offset of the soname in .dynstr.
+  // Since niether can be found prior to final layout we will wait until here
+  // to add them.
+  std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) {
+    std::vector<Elf_Dyn> ret;
+    for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+      if (it->section_) {
+        // We are adding an address relative to a section.
+        ret.push_back(
+            {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
+      } else {
+        ret.push_back({it->tag_, {it->off_}});
+      }
+    }
+    ret.push_back({DT_STRSZ, {strsz}});
+    ret.push_back({DT_SONAME, {soname}});
+    ret.push_back({DT_NULL, {0}});
+    return ret;
+  }
+
+ protected:
+  struct ElfDynamicState {
+    ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+    Elf_Sword tag_;
+    Elf_Word off_;
+  };
+  std::vector<ElfDynamicState> dynamics_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfRawSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+                       const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* link, Elf_Word info,
+                       Elf_Word align, Elf_Word entsize)
+    : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
+                                                       entsize) {}
+  ~ElfRawSectionBuilder() {}
+  std::vector<uint8_t>* GetBuffer() { return &buf_; }
+  void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
+
+ protected:
+  std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
+                       Elf_Word type, Elf_Word flags)
+    : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, nullptr, 0, kPageSize,
+                                                       0), offset_(offset), size_(size) {}
+  ~ElfOatSectionBuilder() {}
+
+  Elf_Word GetOffset() {
+    return offset_;
+  }
+
+  Elf_Word GetSize() {
+    return size_;
+  }
+
+ protected:
+  // Offset of the content within the file.
+  Elf_Word offset_;
+  // Size of the content within the file.
+  Elf_Word size_;
+};
+
+static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+  return ((binding) << 4) + ((type) & 0xf);
+}
+
+// from bionic
+static inline unsigned elfhash(const char *_name) {
+  const unsigned char *name = (const unsigned char *) _name;
+  unsigned h = 0, g;
+
+  while (*name) {
+    h = (h << 4) + *name++;
+    g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+  return h;
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Sym,
+          typename Elf_Shdr>
+class ElfSymtabBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+  // Add a symbol with given name to this symtab. The symbol refers to
+  // 'relative_addr' within the given section and has the given attributes.
+  void AddSymbol(const std::string& name,
+                 const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section,
+                 Elf_Addr addr,
+                 bool is_relative,
+                 Elf_Word size,
+                 uint8_t binding,
+                 uint8_t type,
+                 uint8_t other = 0) {
+    CHECK(section);
+    ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
+                                            MakeStInfo(binding, type), other, 0};
+    symbols_.push_back(state);
+  }
+
+  ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
+                   const std::string& str_name, Elf_Word str_type, bool alloc)
+  : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
+                                                     &strtab_, 0, sizeof(Elf_Word),
+                                                     sizeof(Elf_Sym)), str_name_(str_name),
+                                                     str_type_(str_type),
+                                                     strtab_(str_name,
+                                                             str_type,
+                                                             ((alloc) ? SHF_ALLOC : 0U),
+                                                             nullptr, 0, 1, 1) {}
+  ~ElfSymtabBuilder() {}
+
+  std::vector<Elf_Word> GenerateHashContents() {
+    // Here is how The ELF hash table works.
+    // There are 3 arrays to worry about.
+    // * The symbol table where the symbol information is.
+    // * The bucket array which is an array of indexes into the symtab and chain.
+    // * The chain array which is also an array of indexes into the symtab and chain.
+    //
+    // Lets say the state is something like this.
+    // +--------+       +--------+      +-----------+
+    // | symtab |       | bucket |      |   chain   |
+    // |  null  |       | 1      |      | STN_UNDEF |
+    // | <sym1> |       | 4      |      | 2         |
+    // | <sym2> |       |        |      | 5         |
+    // | <sym3> |       |        |      | STN_UNDEF |
+    // | <sym4> |       |        |      | 3         |
+    // | <sym5> |       |        |      | STN_UNDEF |
+    // +--------+       +--------+      +-----------+
+    //
+    // The lookup process (in python psudocode) is
+    //
+    // def GetSym(name):
+    //     # NB STN_UNDEF == 0
+    //     indx = bucket[elfhash(name) % num_buckets]
+    //     while indx != STN_UNDEF:
+    //         if GetSymbolName(symtab[indx]) == name:
+    //             return symtab[indx]
+    //         indx = chain[indx]
+    //     return SYMBOL_NOT_FOUND
+    //
+    // Between bucket and chain arrays every symtab index must be present exactly
+    // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+
+    // Select number of buckets.
+    // This is essentially arbitrary.
+    Elf_Word nbuckets;
+    Elf_Word chain_size = GetSize();
+    if (symbols_.size() < 8) {
+      nbuckets = 2;
+    } else if (symbols_.size() < 32) {
+      nbuckets = 4;
+    } else if (symbols_.size() < 256) {
+      nbuckets = 16;
+    } else {
+      // Have about 32 ids per bucket.
+      nbuckets = RoundUp(symbols_.size()/32, 2);
+    }
+    std::vector<Elf_Word> hash;
+    hash.push_back(nbuckets);
+    hash.push_back(chain_size);
+    uint32_t bucket_offset = hash.size();
+    uint32_t chain_offset = bucket_offset + nbuckets;
+    hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+    Elf_Word* buckets = hash.data() + bucket_offset;
+    Elf_Word* chain   = hash.data() + chain_offset;
+
+    // Set up the actual hash table.
+    for (Elf_Word i = 0; i < symbols_.size(); i++) {
+      // Add 1 since we need to have the null symbol that is not in the symbols
+      // list.
+      Elf_Word index = i + 1;
+      Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
+      if (buckets[hash_val] == 0) {
+        buckets[hash_val] = index;
+      } else {
+        hash_val = buckets[hash_val];
+        CHECK_LT(hash_val, chain_size);
+        while (chain[hash_val] != 0) {
+          hash_val = chain[hash_val];
+          CHECK_LT(hash_val, chain_size);
+        }
+        chain[hash_val] = index;
+        // Check for loops. Works because if this is non-empty then there must be
+        // another cell which already contains the same symbol index as this one,
+        // which means some symbol has more then one name, which isn't allowed.
+        CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+      }
+    }
+
+    return hash;
+  }
+
+  std::string GenerateStrtab() {
+    std::string tab;
+    tab += '\0';
+    for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
+      it->name_idx_ = tab.size();
+      tab += it->name_;
+      tab += '\0';
+    }
+    strtab_.section_.sh_size = tab.size();
+    return tab;
+  }
+
+  std::vector<Elf_Sym> GenerateSymtab() {
+    std::vector<Elf_Sym> ret;
+    Elf_Sym undef_sym;
+    memset(&undef_sym, 0, sizeof(undef_sym));
+    undef_sym.st_shndx = SHN_UNDEF;
+    ret.push_back(undef_sym);
+
+    for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
+      Elf_Sym sym;
+      memset(&sym, 0, sizeof(sym));
+      sym.st_name = it->name_idx_;
+      if (it->is_relative_) {
+        sym.st_value = it->addr_ + it->section_->section_.sh_offset;
+      } else {
+        sym.st_value = it->addr_;
+      }
+      sym.st_size = it->size_;
+      sym.st_other = it->other_;
+      sym.st_shndx = it->section_->section_index_;
+      sym.st_info = it->info_;
+
+      ret.push_back(sym);
+    }
+    return ret;
+  }
+
+  Elf_Word GetSize() {
+    // 1 is for the implicit NULL symbol.
+    return symbols_.size() + 1;
+  }
+
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+    return &strtab_;
+  }
+
+ protected:
+  struct ElfSymbolState {
+    const std::string name_;
+    const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+    Elf_Addr addr_;
+    Elf_Word size_;
+    bool is_relative_;
+    uint8_t info_;
+    uint8_t other_;
+    // Used during Write() to temporarially hold name index in the strtab.
+    Elf_Word name_idx_;
+  };
+
+  // Information for the strsym for dynstr sections.
+  const std::string str_name_;
+  Elf_Word str_type_;
+  // The symbols in the same order they will be in the symbol table.
+  std::vector<ElfSymbolState> symbols_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
+};
+
+class CodeOutput {
+ public:
+  virtual bool Write(OutputStream* out) = 0;
+  virtual ~CodeOutput() {}
+};
+
+template <typename Elf_Word, typename Elf_Shdr>
+static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
+  return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+          typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+class ElfBuilder FINAL {
+ public:
+  ElfBuilder(CodeOutput* oat_writer,
+             File* elf_file,
+             InstructionSet isa,
+             Elf_Word rodata_relative_offset,
+             Elf_Word rodata_size,
+             Elf_Word text_relative_offset,
+             Elf_Word text_size,
+             const bool add_symbols,
+             bool debug = false)
+    : oat_writer_(oat_writer),
+      elf_file_(elf_file),
+      add_symbols_(add_symbols),
+      debug_logging_(debug),
+      text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
+                    SHF_ALLOC | SHF_EXECINSTR),
+      rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
+      dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
+      symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
+      hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
+                    sizeof(Elf_Word)),
+      dynamic_builder_(".dynamic", &dynsym_builder_),
+      shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+    SetupEhdr();
+    SetupDynamic();
+    SetupRequiredSymbols();
+    SetISA(isa);
+  }
+  ~ElfBuilder() {}
+
+  bool Init() {
+    // The basic layout of the elf file. Order may be different in final output.
+    // +-------------------------+
+    // | Elf_Ehdr                |
+    // +-------------------------+
+    // | Elf_Phdr PHDR           |
+    // | Elf_Phdr LOAD R         | .dynsym .dynstr .hash .rodata
+    // | Elf_Phdr LOAD R X       | .text
+    // | Elf_Phdr LOAD RW        | .dynamic
+    // | Elf_Phdr DYNAMIC        | .dynamic
+    // +-------------------------+
+    // | .dynsym                 |
+    // | Elf_Sym  STN_UNDEF      |
+    // | Elf_Sym  oatdata        |
+    // | Elf_Sym  oatexec        |
+    // | Elf_Sym  oatlastword    |
+    // +-------------------------+
+    // | .dynstr                 |
+    // | \0                      |
+    // | oatdata\0               |
+    // | oatexec\0               |
+    // | oatlastword\0           |
+    // | boot.oat\0              |
+    // +-------------------------+
+    // | .hash                   |
+    // | Elf_Word nbucket = b    |
+    // | Elf_Word nchain  = c    |
+    // | Elf_Word bucket[0]      |
+    // |         ...             |
+    // | Elf_Word bucket[b - 1]  |
+    // | Elf_Word chain[0]       |
+    // |         ...             |
+    // | Elf_Word chain[c - 1]   |
+    // +-------------------------+
+    // | .rodata                 |
+    // | oatdata..oatexec-4      |
+    // +-------------------------+
+    // | .text                   |
+    // | oatexec..oatlastword    |
+    // +-------------------------+
+    // | .dynamic                |
+    // | Elf_Dyn DT_SONAME       |
+    // | Elf_Dyn DT_HASH         |
+    // | Elf_Dyn DT_SYMTAB       |
+    // | Elf_Dyn DT_SYMENT       |
+    // | Elf_Dyn DT_STRTAB       |
+    // | Elf_Dyn DT_STRSZ        |
+    // | Elf_Dyn DT_NULL         |
+    // +-------------------------+  (Optional)
+    // | .strtab                 |  (Optional)
+    // | program symbol names    |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .symtab                 |  (Optional)
+    // | program symbols         |  (Optional)
+    // +-------------------------+
+    // | .shstrtab               |
+    // | \0                      |
+    // | .dynamic\0              |
+    // | .dynsym\0               |
+    // | .dynstr\0               |
+    // | .hash\0                 |
+    // | .rodata\0               |
+    // | .text\0                 |
+    // | .shstrtab\0             |
+    // | .symtab\0               |  (Optional)
+    // | .strtab\0               |  (Optional)
+    // | .debug_str\0            |  (Optional)
+    // | .debug_info\0           |  (Optional)
+    // | .eh_frame\0             |  (Optional)
+    // | .debug_line\0           |  (Optional)
+    // | .debug_abbrev\0         |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_info             |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_abbrev           |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .eh_frame               |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_line             |  (Optional)
+    // +-------------------------+  (Optional)
+    // | .debug_str              |  (Optional)
+    // +-------------------------+  (Optional)
+    // | Elf_Shdr NULL           |
+    // | Elf_Shdr .dynsym        |
+    // | Elf_Shdr .dynstr        |
+    // | Elf_Shdr .hash          |
+    // | Elf_Shdr .text          |
+    // | Elf_Shdr .rodata        |
+    // | Elf_Shdr .dynamic       |
+    // | Elf_Shdr .shstrtab      |
+    // | Elf_Shdr .debug_info    |  (Optional)
+    // | Elf_Shdr .debug_abbrev  |  (Optional)
+    // | Elf_Shdr .eh_frame      |  (Optional)
+    // | Elf_Shdr .debug_line    |  (Optional)
+    // | Elf_Shdr .debug_str     |  (Optional)
+    // +-------------------------+
+
+    if (fatal_error_) {
+      return false;
+    }
+    // Step 1. Figure out all the offsets.
+
+    if (debug_logging_) {
+      LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
+      LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+    }
+
+    memset(&program_headers_, 0, sizeof(program_headers_));
+    program_headers_[PH_PHDR].p_type    = PT_PHDR;
+    program_headers_[PH_PHDR].p_offset  = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_vaddr   = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_paddr   = PHDR_OFFSET;
+    program_headers_[PH_PHDR].p_filesz  = sizeof(program_headers_);
+    program_headers_[PH_PHDR].p_memsz   = sizeof(program_headers_);
+    program_headers_[PH_PHDR].p_flags   = PF_R;
+    program_headers_[PH_PHDR].p_align   = sizeof(Elf_Word);
+
+    program_headers_[PH_LOAD_R__].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_R__].p_offset  = 0;
+    program_headers_[PH_LOAD_R__].p_vaddr   = 0;
+    program_headers_[PH_LOAD_R__].p_paddr   = 0;
+    program_headers_[PH_LOAD_R__].p_flags   = PF_R;
+
+    program_headers_[PH_LOAD_R_X].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_R_X].p_flags   = PF_R | PF_X;
+
+    program_headers_[PH_LOAD_RW_].p_type    = PT_LOAD;
+    program_headers_[PH_LOAD_RW_].p_flags   = PF_R | PF_W;
+
+    program_headers_[PH_DYNAMIC].p_type    = PT_DYNAMIC;
+    program_headers_[PH_DYNAMIC].p_flags   = PF_R | PF_W;
+
+    // Get the dynstr string.
+    dynstr_ = dynsym_builder_.GenerateStrtab();
+
+    // Add the SONAME to the dynstr.
+    dynstr_soname_offset_ = dynstr_.size();
+    std::string file_name(elf_file_->GetPath());
+    size_t directory_separator_pos = file_name.rfind('/');
+    if (directory_separator_pos != std::string::npos) {
+      file_name = file_name.substr(directory_separator_pos + 1);
+    }
+    dynstr_ += file_name;
+    dynstr_ += '\0';
+    if (debug_logging_) {
+      LOG(INFO) << "dynstr size (bytes)   =" << dynstr_.size()
+                << std::hex << " " << dynstr_.size();
+      LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
+                << std::hex << " " << dynsym_builder_.GetSize();
+    }
+
+    // Get the section header string table.
+    shstrtab_ += '\0';
+
+    // Setup sym_undef
+    memset(&null_hdr_, 0, sizeof(null_hdr_));
+    null_hdr_.sh_type = SHT_NULL;
+    null_hdr_.sh_link = SHN_UNDEF;
+    section_ptrs_.push_back(&null_hdr_);
+
+    section_index_ = 1;
+
+    // setup .dynsym
+    section_ptrs_.push_back(&dynsym_builder_.section_);
+    AssignSectionStr(&dynsym_builder_, &shstrtab_);
+    dynsym_builder_.section_index_ = section_index_++;
+
+    // Setup .dynstr
+    section_ptrs_.push_back(&dynsym_builder_.GetStrTab()->section_);
+    AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+    dynsym_builder_.GetStrTab()->section_index_ = section_index_++;
+
+    // Setup .hash
+    section_ptrs_.push_back(&hash_builder_.section_);
+    AssignSectionStr(&hash_builder_, &shstrtab_);
+    hash_builder_.section_index_ = section_index_++;
+
+    // Setup .rodata
+    section_ptrs_.push_back(&rodata_builder_.section_);
+    AssignSectionStr(&rodata_builder_, &shstrtab_);
+    rodata_builder_.section_index_ = section_index_++;
+
+    // Setup .text
+    section_ptrs_.push_back(&text_builder_.section_);
+    AssignSectionStr(&text_builder_, &shstrtab_);
+    text_builder_.section_index_ = section_index_++;
+
+    // Setup .dynamic
+    section_ptrs_.push_back(&dynamic_builder_.section_);
+    AssignSectionStr(&dynamic_builder_, &shstrtab_);
+    dynamic_builder_.section_index_ = section_index_++;
+
+    // Fill in the hash section.
+    hash_ = dynsym_builder_.GenerateHashContents();
+
+    if (debug_logging_) {
+      LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
+                << std::hex << " " << hash_.size() * sizeof(Elf_Word);
+    }
+
+    Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
+
+    // Get the layout in the sections.
+    //
+    // Get the layout of the dynsym section.
+    dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
+    dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
+    dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+    dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+    // Get the layout of the dynstr section.
+    dynsym_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                                 (dynsym_builder_.GetStrTab()->section_,
+                                                  dynsym_builder_.section_);
+    dynsym_builder_.GetStrTab()->section_.sh_addr = dynsym_builder_.GetStrTab()->section_.sh_offset;
+    dynsym_builder_.GetStrTab()->section_.sh_size = dynstr_.size();
+    dynsym_builder_.GetStrTab()->section_.sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+    // Get the layout of the hash section
+    hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                       (hash_builder_.section_,
+                                        dynsym_builder_.GetStrTab()->section_);
+    hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
+    hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
+    hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+    // Get the layout of the rodata section.
+    rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                         (rodata_builder_.section_,
+                                          hash_builder_.section_);
+    rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
+    rodata_builder_.section_.sh_size = rodata_builder_.GetSize();
+    rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
+
+    // Get the layout of the text section.
+    text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                       (text_builder_.section_, rodata_builder_.section_);
+    text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
+    text_builder_.section_.sh_size = text_builder_.GetSize();
+    text_builder_.section_.sh_link = text_builder_.GetLink();
+    CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
+
+    // Get the layout of the dynamic section.
+    dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                          (dynamic_builder_.section_,
+                                           text_builder_.section_);
+    dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
+    dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+    dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+    if (debug_logging_) {
+      LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
+                << " dynsym size=" << dynsym_builder_.section_.sh_size;
+      LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->section_.sh_offset
+                << " dynstr size=" << dynsym_builder_.GetStrTab()->section_.sh_size;
+      LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
+                << " hash size=" << hash_builder_.section_.sh_size;
+      LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
+                << " rodata size=" << rodata_builder_.section_.sh_size;
+      LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
+                << " text size=" << text_builder_.section_.sh_size;
+      LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
+                << " dynamic size=" << dynamic_builder_.section_.sh_size;
+    }
+
+    return true;
+  }
+
+  bool Write() {
+    std::vector<ElfFilePiece> pieces;
+    Elf_Shdr prev = dynamic_builder_.section_;
+    std::string strtab;
+
+    if (IncludingDebugSymbols()) {
+      // Setup .symtab
+      section_ptrs_.push_back(&symtab_builder_.section_);
+      AssignSectionStr(&symtab_builder_, &shstrtab_);
+      symtab_builder_.section_index_ = section_index_++;
+
+      // Setup .strtab
+      section_ptrs_.push_back(&symtab_builder_.GetStrTab()->section_);
+      AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+      symtab_builder_.GetStrTab()->section_index_ = section_index_++;
+
+      strtab = symtab_builder_.GenerateStrtab();
+      if (debug_logging_) {
+        LOG(INFO) << "strtab size (bytes)    =" << strtab.size()
+                  << std::hex << " " << strtab.size();
+        LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
+                  << std::hex << " " << symtab_builder_.GetSize();
+      }
+    }
+
+    // Setup all the other sections.
+    for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(),
+         *end = builder + other_builders_.size();
+         builder != end; ++builder) {
+      section_ptrs_.push_back(&builder->section_);
+      AssignSectionStr(builder, &shstrtab_);
+      builder->section_index_ = section_index_++;
+    }
+
+    // Setup shstrtab
+    section_ptrs_.push_back(&shstrtab_builder_.section_);
+    AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+    shstrtab_builder_.section_index_ = section_index_++;
+
+    if (debug_logging_) {
+      LOG(INFO) << ".shstrtab size    (bytes)   =" << shstrtab_.size()
+                << std::hex << " " << shstrtab_.size();
+      LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
+                << std::hex << " " << section_ptrs_.size();
+    }
+
+    if (IncludingDebugSymbols()) {
+      // Get the layout of the symtab section.
+      symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                           (symtab_builder_.section_,
+                                            dynamic_builder_.section_);
+      symtab_builder_.section_.sh_addr = 0;
+      // Add to leave space for the null symbol.
+      symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+      symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+      // Get the layout of the dynstr section.
+      symtab_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                                   (symtab_builder_.GetStrTab()->section_,
+                                                    symtab_builder_.section_);
+      symtab_builder_.GetStrTab()->section_.sh_addr = 0;
+      symtab_builder_.GetStrTab()->section_.sh_size = strtab.size();
+      symtab_builder_.GetStrTab()->section_.sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+      prev = symtab_builder_.GetStrTab()->section_;
+      if (debug_logging_) {
+        LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
+                  << " symtab size=" << symtab_builder_.section_.sh_size;
+        LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->section_.sh_offset
+                  << " strtab size=" << symtab_builder_.GetStrTab()->section_.sh_size;
+      }
+    }
+
+    // Get the layout of the extra sections. (This will deal with the debug
+    // sections if they are there)
+    for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+      it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
+      it->section_.sh_addr = 0;
+      it->section_.sh_size = it->GetBuffer()->size();
+      it->section_.sh_link = it->GetLink();
+      pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
+                                    it->GetBuffer()->data(), it->GetBuffer()->size()));
+      prev = it->section_;
+      if (debug_logging_) {
+        LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
+                  << " size=" << it->section_.sh_size;
+      }
+    }
+
+    // Get the layout of the shstrtab section
+    shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+                                           (shstrtab_builder_.section_, prev);
+    shstrtab_builder_.section_.sh_addr = 0;
+    shstrtab_builder_.section_.sh_size = shstrtab_.size();
+    shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
+    if (debug_logging_) {
+        LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
+                  << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
+    }
+
+    // The section list comes after come after.
+    Elf_Word sections_offset = RoundUp(
+        shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+        sizeof(Elf_Word));
+
+    // Setup the actual symbol arrays.
+    std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+    CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
+    std::vector<Elf_Sym> symtab;
+    if (IncludingDebugSymbols()) {
+      symtab = symtab_builder_.GenerateSymtab();
+      CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
+    }
+
+    // Setup the dynamic section.
+    // This will add the 2 values we cannot know until now time, namely the size
+    // and the soname_offset.
+    std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
+                                                                  dynstr_soname_offset_);
+    CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
+
+    // Finish setup of the program headers now that we know the layout of the
+    // whole file.
+    Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
+    program_headers_[PH_LOAD_R__].p_memsz =  load_r_size;
+    program_headers_[PH_LOAD_R__].p_align =  rodata_builder_.section_.sh_addralign;
+
+    Elf_Word load_rx_size = text_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_vaddr  = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_paddr  = text_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
+    program_headers_[PH_LOAD_R_X].p_memsz  = load_rx_size;
+    program_headers_[PH_LOAD_R_X].p_align  = text_builder_.section_.sh_addralign;
+
+    program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_vaddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_paddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_RW_].p_memsz  = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_LOAD_RW_].p_align  = dynamic_builder_.section_.sh_addralign;
+
+    program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_vaddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_paddr  = dynamic_builder_.section_.sh_offset;
+    program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_DYNAMIC].p_memsz  = dynamic_builder_.section_.sh_size;
+    program_headers_[PH_DYNAMIC].p_align  = dynamic_builder_.section_.sh_addralign;
+
+    // Finish setup of the Ehdr values.
+    elf_header_.e_phoff = PHDR_OFFSET;
+    elf_header_.e_shoff = sections_offset;
+    elf_header_.e_phnum = PH_NUM;
+    elf_header_.e_shnum = section_ptrs_.size();
+    elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
+
+    // Add the rest of the pieces to the list.
+    pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
+    pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
+                                  &program_headers_, sizeof(program_headers_)));
+    pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
+                                  dynamic.data(), dynamic_builder_.section_.sh_size));
+    pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
+                                  dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
+    pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.GetStrTab()->section_.sh_offset,
+                                  dynstr_.c_str(), dynstr_.size()));
+    pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
+                                  hash_.data(), hash_.size() * sizeof(Elf_Word)));
+    pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
+                                  nullptr, rodata_builder_.section_.sh_size));
+    pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
+                                  nullptr, text_builder_.section_.sh_size));
+    if (IncludingDebugSymbols()) {
+      pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
+                                    symtab.data(), symtab.size() * sizeof(Elf_Sym)));
+      pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.GetStrTab()->section_.sh_offset,
+                                    strtab.c_str(), strtab.size()));
+    }
+    pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
+                                  &shstrtab_[0], shstrtab_.size()));
+    for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
+      // Just add all the sections in induvidually since they are all over the
+      // place on the heap/stack.
+      Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
+      pieces.push_back(ElfFilePiece("section table piece", cur_off,
+                                    section_ptrs_[i], sizeof(Elf_Shdr)));
+    }
+
+    if (!WriteOutFile(pieces)) {
+      LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
+      return false;
+    }
+    // write out the actual oat file data.
+    Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
+    if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
+      PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
+                  << " for " << elf_file_->GetPath();
+      return false;
+    }
+    std::unique_ptr<BufferedOutputStream> output_stream(
+        new BufferedOutputStream(new FileOutputStream(elf_file_)));
+    if (!oat_writer_->Write(output_stream.get())) {
+      PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
+      return false;
+    }
+
+    return true;
+  }
+
+  // Adds the given raw section to the builder. This will copy it. The caller
+  // is responsible for deallocating their copy.
+  void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) {
+    other_builders_.push_back(bld);
+  }
+
+ private:
+  CodeOutput* oat_writer_;
+  File* elf_file_;
+  const bool add_symbols_;
+  const bool debug_logging_;
+
+  bool fatal_error_ = false;
+
+  // What phdr is.
+  static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
+  enum : uint8_t {
+    PH_PHDR     = 0,
+        PH_LOAD_R__ = 1,
+        PH_LOAD_R_X = 2,
+        PH_LOAD_RW_ = 3,
+        PH_DYNAMIC  = 4,
+        PH_NUM      = 5,
+  };
+  static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
+  Elf_Phdr program_headers_[PH_NUM];
+
+  Elf_Ehdr elf_header_;
+
+  Elf_Shdr null_hdr_;
+  std::string shstrtab_;
+  uint32_t section_index_;
+  std::string dynstr_;
+  uint32_t dynstr_soname_offset_;
+  std::vector<Elf_Shdr*> section_ptrs_;
+  std::vector<Elf_Word> hash_;
+
+ public:
+  ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> text_builder_;
+  ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> rodata_builder_;
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> dynsym_builder_;
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> symtab_builder_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_;
+  ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_;
+  ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_;
+  std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_;
+
+ private:
+  void SetISA(InstructionSet isa) {
+    switch (isa) {
+      case kArm:
+        // Fall through.
+      case kThumb2: {
+        elf_header_.e_machine = EM_ARM;
+        elf_header_.e_flags = EF_ARM_EABI_VER5;
+        break;
+      }
+      case kArm64: {
+        elf_header_.e_machine = EM_AARCH64;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kX86: {
+        elf_header_.e_machine = EM_386;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kX86_64: {
+        elf_header_.e_machine = EM_X86_64;
+        elf_header_.e_flags = 0;
+        break;
+      }
+      case kMips: {
+        elf_header_.e_machine = EM_MIPS;
+        elf_header_.e_flags = (EF_MIPS_NOREORDER |
+                               EF_MIPS_PIC       |
+                               EF_MIPS_CPIC      |
+                               EF_MIPS_ABI_O32   |
+                               EF_MIPS_ARCH_32R2);
+        break;
+      }
+      default: {
+        fatal_error_ = true;
+        LOG(FATAL) << "Unknown instruction set: " << isa;
+        break;
+      }
+    }
+  }
+
+  void SetupEhdr() {
+    memset(&elf_header_, 0, sizeof(elf_header_));
+    elf_header_.e_ident[EI_MAG0]       = ELFMAG0;
+    elf_header_.e_ident[EI_MAG1]       = ELFMAG1;
+    elf_header_.e_ident[EI_MAG2]       = ELFMAG2;
+    elf_header_.e_ident[EI_MAG3]       = ELFMAG3;
+    elf_header_.e_ident[EI_CLASS]      = ELFCLASS32;
+    elf_header_.e_ident[EI_DATA]       = ELFDATA2LSB;
+    elf_header_.e_ident[EI_VERSION]    = EV_CURRENT;
+    elf_header_.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
+    elf_header_.e_ident[EI_ABIVERSION] = 0;
+    elf_header_.e_type = ET_DYN;
+    elf_header_.e_version = 1;
+    elf_header_.e_entry = 0;
+    elf_header_.e_ehsize = sizeof(Elf_Ehdr);
+    elf_header_.e_phentsize = sizeof(Elf_Phdr);
+    elf_header_.e_shentsize = sizeof(Elf_Shdr);
+    elf_header_.e_phoff = sizeof(Elf_Ehdr);
+  }
+
+  // Sets up a bunch of the required Dynamic Section entries.
+  // Namely it will initialize all the mandatory ones that it can.
+  // Specifically:
+  // DT_HASH
+  // DT_STRTAB
+  // DT_SYMTAB
+  // DT_SYMENT
+  //
+  // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
+  void SetupDynamic() {
+    dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
+    dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
+    dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
+    dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+  }
+
+  // Sets up the basic dynamic symbols that are needed, namely all those we
+  // can know already.
+  //
+  // Specifically adds:
+  // oatdata
+  // oatexec
+  // oatlastword
+  void SetupRequiredSymbols() {
+    dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
+                              rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
+                              text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+    dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
+                              true, 4, STB_GLOBAL, STT_OBJECT);
+  }
+
+  void AssignSectionStr(ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder,
+                        std::string* strtab) {
+    builder->section_.sh_name = strtab->size();
+    *strtab += builder->name_;
+    *strtab += '\0';
+    if (debug_logging_) {
+      LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
+                << "to shstrtab at offset " << builder->section_.sh_name;
+    }
+  }
+
+  struct ElfFilePiece {
+    ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+    : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
+    ~ElfFilePiece() {}
+
+    const std::string& dbg_name_;
+    Elf_Word offset_;
+    const void *data_;
+    Elf_Word size_;
+    static bool Compare(ElfFilePiece a, ElfFilePiece b) {
+      return a.offset_ < b.offset_;
+    }
+  };
+
+  // Write each of the pieces out to the file.
+  bool WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
+    // TODO It would be nice if this checked for overlap.
+    for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+      if (it->data_) {
+        if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
+          PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
+                      << it->offset_ << " for " << elf_file_->GetPath();
+          return false;
+        }
+        if (!elf_file_->WriteFully(it->data_, it->size_)) {
+          PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 4c69fc8..dbd3a37 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -23,6 +23,7 @@
 #include "buffered_output_stream.h"
 #include "driver/compiler_driver.h"
 #include "dwarf.h"
+#include "elf_builder.h"
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "file_output_stream.h"
@@ -34,15 +35,6 @@
 
 namespace art {
 
-template <typename Elf_Word, typename Elf_Shdr>
-static constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
-  return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
-}
-
-static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
-  return ((binding) << 4) + ((type) & 0xf);
-}
-
 static void PushByte(std::vector<uint8_t>* buf, int data) {
   buf->push_back(data & 0xff);
 }
@@ -83,826 +75,6 @@
           typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
           typename Elf_Phdr, typename Elf_Shdr>
 bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Init() {
-  // The basic layout of the elf file. Order may be different in final output.
-  // +-------------------------+
-  // | Elf_Ehdr                |
-  // +-------------------------+
-  // | Elf_Phdr PHDR           |
-  // | Elf_Phdr LOAD R         | .dynsym .dynstr .hash .rodata
-  // | Elf_Phdr LOAD R X       | .text
-  // | Elf_Phdr LOAD RW        | .dynamic
-  // | Elf_Phdr DYNAMIC        | .dynamic
-  // +-------------------------+
-  // | .dynsym                 |
-  // | Elf_Sym  STN_UNDEF      |
-  // | Elf_Sym  oatdata        |
-  // | Elf_Sym  oatexec        |
-  // | Elf_Sym  oatlastword    |
-  // +-------------------------+
-  // | .dynstr                 |
-  // | \0                      |
-  // | oatdata\0               |
-  // | oatexec\0               |
-  // | oatlastword\0           |
-  // | boot.oat\0              |
-  // +-------------------------+
-  // | .hash                   |
-  // | Elf_Word nbucket = b    |
-  // | Elf_Word nchain  = c    |
-  // | Elf_Word bucket[0]      |
-  // |         ...             |
-  // | Elf_Word bucket[b - 1]  |
-  // | Elf_Word chain[0]       |
-  // |         ...             |
-  // | Elf_Word chain[c - 1]   |
-  // +-------------------------+
-  // | .rodata                 |
-  // | oatdata..oatexec-4      |
-  // +-------------------------+
-  // | .text                   |
-  // | oatexec..oatlastword    |
-  // +-------------------------+
-  // | .dynamic                |
-  // | Elf_Dyn DT_SONAME       |
-  // | Elf_Dyn DT_HASH         |
-  // | Elf_Dyn DT_SYMTAB       |
-  // | Elf_Dyn DT_SYMENT       |
-  // | Elf_Dyn DT_STRTAB       |
-  // | Elf_Dyn DT_STRSZ        |
-  // | Elf_Dyn DT_NULL         |
-  // +-------------------------+  (Optional)
-  // | .strtab                 |  (Optional)
-  // | program symbol names    |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .symtab                 |  (Optional)
-  // | program symbols         |  (Optional)
-  // +-------------------------+
-  // | .shstrtab               |
-  // | \0                      |
-  // | .dynamic\0              |
-  // | .dynsym\0               |
-  // | .dynstr\0               |
-  // | .hash\0                 |
-  // | .rodata\0               |
-  // | .text\0                 |
-  // | .shstrtab\0             |
-  // | .symtab\0               |  (Optional)
-  // | .strtab\0               |  (Optional)
-  // | .debug_str\0            |  (Optional)
-  // | .debug_info\0           |  (Optional)
-  // | .eh_frame\0             |  (Optional)
-  // | .debug_line\0           |  (Optional)
-  // | .debug_abbrev\0         |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_info             |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_abbrev           |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .eh_frame               |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_line             |  (Optional)
-  // +-------------------------+  (Optional)
-  // | .debug_str              |  (Optional)
-  // +-------------------------+  (Optional)
-  // | Elf_Shdr NULL           |
-  // | Elf_Shdr .dynsym        |
-  // | Elf_Shdr .dynstr        |
-  // | Elf_Shdr .hash          |
-  // | Elf_Shdr .text          |
-  // | Elf_Shdr .rodata        |
-  // | Elf_Shdr .dynamic       |
-  // | Elf_Shdr .shstrtab      |
-  // | Elf_Shdr .debug_info    |  (Optional)
-  // | Elf_Shdr .debug_abbrev  |  (Optional)
-  // | Elf_Shdr .eh_frame      |  (Optional)
-  // | Elf_Shdr .debug_line    |  (Optional)
-  // | Elf_Shdr .debug_str     |  (Optional)
-  // +-------------------------+
-
-  if (fatal_error_) {
-    return false;
-  }
-  // Step 1. Figure out all the offsets.
-
-  if (debug_logging_) {
-    LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
-    LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
-  }
-
-  memset(&program_headers_, 0, sizeof(program_headers_));
-  program_headers_[PH_PHDR].p_type    = PT_PHDR;
-  program_headers_[PH_PHDR].p_offset  = PHDR_OFFSET;
-  program_headers_[PH_PHDR].p_vaddr   = PHDR_OFFSET;
-  program_headers_[PH_PHDR].p_paddr   = PHDR_OFFSET;
-  program_headers_[PH_PHDR].p_filesz  = sizeof(program_headers_);
-  program_headers_[PH_PHDR].p_memsz   = sizeof(program_headers_);
-  program_headers_[PH_PHDR].p_flags   = PF_R;
-  program_headers_[PH_PHDR].p_align   = sizeof(Elf_Word);
-
-  program_headers_[PH_LOAD_R__].p_type    = PT_LOAD;
-  program_headers_[PH_LOAD_R__].p_offset  = 0;
-  program_headers_[PH_LOAD_R__].p_vaddr   = 0;
-  program_headers_[PH_LOAD_R__].p_paddr   = 0;
-  program_headers_[PH_LOAD_R__].p_flags   = PF_R;
-
-  program_headers_[PH_LOAD_R_X].p_type    = PT_LOAD;
-  program_headers_[PH_LOAD_R_X].p_flags   = PF_R | PF_X;
-
-  program_headers_[PH_LOAD_RW_].p_type    = PT_LOAD;
-  program_headers_[PH_LOAD_RW_].p_flags   = PF_R | PF_W;
-
-  program_headers_[PH_DYNAMIC].p_type    = PT_DYNAMIC;
-  program_headers_[PH_DYNAMIC].p_flags   = PF_R | PF_W;
-
-  // Get the dynstr string.
-  dynstr_ = dynsym_builder_.GenerateStrtab();
-
-  // Add the SONAME to the dynstr.
-  dynstr_soname_offset_ = dynstr_.size();
-  std::string file_name(elf_file_->GetPath());
-  size_t directory_separator_pos = file_name.rfind('/');
-  if (directory_separator_pos != std::string::npos) {
-    file_name = file_name.substr(directory_separator_pos + 1);
-  }
-  dynstr_ += file_name;
-  dynstr_ += '\0';
-  if (debug_logging_) {
-    LOG(INFO) << "dynstr size (bytes)   =" << dynstr_.size()
-              << std::hex << " " << dynstr_.size();
-    LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
-              << std::hex << " " << dynsym_builder_.GetSize();
-  }
-
-  // Get the section header string table.
-  shstrtab_ += '\0';
-
-  // Setup sym_undef
-  memset(&null_hdr_, 0, sizeof(null_hdr_));
-  null_hdr_.sh_type = SHT_NULL;
-  null_hdr_.sh_link = SHN_UNDEF;
-  section_ptrs_.push_back(&null_hdr_);
-
-  section_index_ = 1;
-
-  // setup .dynsym
-  section_ptrs_.push_back(&dynsym_builder_.section_);
-  AssignSectionStr(&dynsym_builder_, &shstrtab_);
-  dynsym_builder_.section_index_ = section_index_++;
-
-  // Setup .dynstr
-  section_ptrs_.push_back(&dynsym_builder_.strtab_.section_);
-  AssignSectionStr(&dynsym_builder_.strtab_, &shstrtab_);
-  dynsym_builder_.strtab_.section_index_ = section_index_++;
-
-  // Setup .hash
-  section_ptrs_.push_back(&hash_builder_.section_);
-  AssignSectionStr(&hash_builder_, &shstrtab_);
-  hash_builder_.section_index_ = section_index_++;
-
-  // Setup .rodata
-  section_ptrs_.push_back(&rodata_builder_.section_);
-  AssignSectionStr(&rodata_builder_, &shstrtab_);
-  rodata_builder_.section_index_ = section_index_++;
-
-  // Setup .text
-  section_ptrs_.push_back(&text_builder_.section_);
-  AssignSectionStr(&text_builder_, &shstrtab_);
-  text_builder_.section_index_ = section_index_++;
-
-  // Setup .dynamic
-  section_ptrs_.push_back(&dynamic_builder_.section_);
-  AssignSectionStr(&dynamic_builder_, &shstrtab_);
-  dynamic_builder_.section_index_ = section_index_++;
-
-  // Fill in the hash section.
-  hash_ = dynsym_builder_.GenerateHashContents();
-
-  if (debug_logging_) {
-    LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
-              << std::hex << " " << hash_.size() * sizeof(Elf_Word);
-  }
-
-  Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
-
-  // Get the layout in the sections.
-  //
-  // Get the layout of the dynsym section.
-  dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
-  dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
-  dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
-  dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
-
-  // Get the layout of the dynstr section.
-  dynsym_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                               (dynsym_builder_.strtab_.section_,
-                                                dynsym_builder_.section_);
-  dynsym_builder_.strtab_.section_.sh_addr = dynsym_builder_.strtab_.section_.sh_offset;
-  dynsym_builder_.strtab_.section_.sh_size = dynstr_.size();
-  dynsym_builder_.strtab_.section_.sh_link = dynsym_builder_.strtab_.GetLink();
-
-  // Get the layout of the hash section
-  hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                     (hash_builder_.section_,
-                                      dynsym_builder_.strtab_.section_);
-  hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
-  hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
-  hash_builder_.section_.sh_link = hash_builder_.GetLink();
-
-  // Get the layout of the rodata section.
-  rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                       (rodata_builder_.section_,
-                                        hash_builder_.section_);
-  rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
-  rodata_builder_.section_.sh_size = rodata_builder_.size_;
-  rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
-
-  // Get the layout of the text section.
-  text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                     (text_builder_.section_, rodata_builder_.section_);
-  text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
-  text_builder_.section_.sh_size = text_builder_.size_;
-  text_builder_.section_.sh_link = text_builder_.GetLink();
-  CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
-
-  // Get the layout of the dynamic section.
-  dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                        (dynamic_builder_.section_,
-                                         text_builder_.section_);
-  dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
-  dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
-  dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
-
-  if (debug_logging_) {
-    LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
-              << " dynsym size=" << dynsym_builder_.section_.sh_size;
-    LOG(INFO) << "dynstr off=" << dynsym_builder_.strtab_.section_.sh_offset
-              << " dynstr size=" << dynsym_builder_.strtab_.section_.sh_size;
-    LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
-              << " hash size=" << hash_builder_.section_.sh_size;
-    LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
-              << " rodata size=" << rodata_builder_.section_.sh_size;
-    LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
-              << " text size=" << text_builder_.section_.sh_size;
-    LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
-              << " dynamic size=" << dynamic_builder_.section_.sh_size;
-  }
-
-  return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Write() {
-  std::vector<ElfFilePiece> pieces;
-  Elf_Shdr prev = dynamic_builder_.section_;
-  std::string strtab;
-
-  if (IncludingDebugSymbols()) {
-    // Setup .symtab
-    section_ptrs_.push_back(&symtab_builder_.section_);
-    AssignSectionStr(&symtab_builder_, &shstrtab_);
-    symtab_builder_.section_index_ = section_index_++;
-
-    // Setup .strtab
-    section_ptrs_.push_back(&symtab_builder_.strtab_.section_);
-    AssignSectionStr(&symtab_builder_.strtab_, &shstrtab_);
-    symtab_builder_.strtab_.section_index_ = section_index_++;
-
-    strtab = symtab_builder_.GenerateStrtab();
-    if (debug_logging_) {
-      LOG(INFO) << "strtab size (bytes)    =" << strtab.size()
-                << std::hex << " " << strtab.size();
-      LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
-                << std::hex << " " << symtab_builder_.GetSize();
-    }
-  }
-
-  // Setup all the other sections.
-  for (ElfRawSectionBuilder *builder = other_builders_.data(),
-                            *end = builder + other_builders_.size();
-       builder != end; ++builder) {
-    section_ptrs_.push_back(&builder->section_);
-    AssignSectionStr(builder, &shstrtab_);
-    builder->section_index_ = section_index_++;
-  }
-
-  // Setup shstrtab
-  section_ptrs_.push_back(&shstrtab_builder_.section_);
-  AssignSectionStr(&shstrtab_builder_, &shstrtab_);
-  shstrtab_builder_.section_index_ = section_index_++;
-
-  if (debug_logging_) {
-    LOG(INFO) << ".shstrtab size    (bytes)   =" << shstrtab_.size()
-              << std::hex << " " << shstrtab_.size();
-    LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
-              << std::hex << " " << section_ptrs_.size();
-  }
-
-  if (IncludingDebugSymbols()) {
-    // Get the layout of the symtab section.
-    symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                         (symtab_builder_.section_,
-                                          dynamic_builder_.section_);
-    symtab_builder_.section_.sh_addr = 0;
-    // Add to leave space for the null symbol.
-    symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
-    symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
-
-    // Get the layout of the dynstr section.
-    symtab_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                                 (symtab_builder_.strtab_.section_,
-                                                  symtab_builder_.section_);
-    symtab_builder_.strtab_.section_.sh_addr = 0;
-    symtab_builder_.strtab_.section_.sh_size = strtab.size();
-    symtab_builder_.strtab_.section_.sh_link = symtab_builder_.strtab_.GetLink();
-
-    prev = symtab_builder_.strtab_.section_;
-    if (debug_logging_) {
-      LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
-                << " symtab size=" << symtab_builder_.section_.sh_size;
-      LOG(INFO) << "strtab off=" << symtab_builder_.strtab_.section_.sh_offset
-                << " strtab size=" << symtab_builder_.strtab_.section_.sh_size;
-    }
-  }
-
-  // Get the layout of the extra sections. (This will deal with the debug
-  // sections if they are there)
-  for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
-    it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
-    it->section_.sh_addr = 0;
-    it->section_.sh_size = it->GetBuffer()->size();
-    it->section_.sh_link = it->GetLink();
-    pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
-                                  it->GetBuffer()->data(), it->GetBuffer()->size()));
-    prev = it->section_;
-    if (debug_logging_) {
-      LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
-                << " " << it->name_ << " size=" << it->section_.sh_size;
-    }
-  }
-
-  // Get the layout of the shstrtab section
-  shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
-                                         (shstrtab_builder_.section_, prev);
-  shstrtab_builder_.section_.sh_addr = 0;
-  shstrtab_builder_.section_.sh_size = shstrtab_.size();
-  shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
-  if (debug_logging_) {
-      LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
-                << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
-  }
-
-  // The section list comes after come after.
-  Elf_Word sections_offset = RoundUp(
-      shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
-      sizeof(Elf_Word));
-
-  // Setup the actual symbol arrays.
-  std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
-  CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
-  std::vector<Elf_Sym> symtab;
-  if (IncludingDebugSymbols()) {
-    symtab = symtab_builder_.GenerateSymtab();
-    CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
-  }
-
-  // Setup the dynamic section.
-  // This will add the 2 values we cannot know until now time, namely the size
-  // and the soname_offset.
-  std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
-                                                                dynstr_soname_offset_);
-  CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
-
-  // Finish setup of the program headers now that we know the layout of the
-  // whole file.
-  Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
-  program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
-  program_headers_[PH_LOAD_R__].p_memsz =  load_r_size;
-  program_headers_[PH_LOAD_R__].p_align =  rodata_builder_.section_.sh_addralign;
-
-  Elf_Word load_rx_size = text_builder_.section_.sh_size;
-  program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_R_X].p_vaddr  = text_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_R_X].p_paddr  = text_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
-  program_headers_[PH_LOAD_R_X].p_memsz  = load_rx_size;
-  program_headers_[PH_LOAD_R_X].p_align  = text_builder_.section_.sh_addralign;
-
-  program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_RW_].p_vaddr  = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_RW_].p_paddr  = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
-  program_headers_[PH_LOAD_RW_].p_memsz  = dynamic_builder_.section_.sh_size;
-  program_headers_[PH_LOAD_RW_].p_align  = dynamic_builder_.section_.sh_addralign;
-
-  program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_DYNAMIC].p_vaddr  = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_DYNAMIC].p_paddr  = dynamic_builder_.section_.sh_offset;
-  program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
-  program_headers_[PH_DYNAMIC].p_memsz  = dynamic_builder_.section_.sh_size;
-  program_headers_[PH_DYNAMIC].p_align  = dynamic_builder_.section_.sh_addralign;
-
-  // Finish setup of the Ehdr values.
-  elf_header_.e_phoff = PHDR_OFFSET;
-  elf_header_.e_shoff = sections_offset;
-  elf_header_.e_phnum = PH_NUM;
-  elf_header_.e_shnum = section_ptrs_.size();
-  elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
-
-  // Add the rest of the pieces to the list.
-  pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
-  pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
-                                &program_headers_, sizeof(program_headers_)));
-  pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
-                                dynamic.data(), dynamic_builder_.section_.sh_size));
-  pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
-                                dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
-  pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.strtab_.section_.sh_offset,
-                                dynstr_.c_str(), dynstr_.size()));
-  pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
-                                hash_.data(), hash_.size() * sizeof(Elf_Word)));
-  pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
-                                nullptr, rodata_builder_.section_.sh_size));
-  pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
-                                nullptr, text_builder_.section_.sh_size));
-  if (IncludingDebugSymbols()) {
-    pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
-                                  symtab.data(), symtab.size() * sizeof(Elf_Sym)));
-    pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.strtab_.section_.sh_offset,
-                                  strtab.c_str(), strtab.size()));
-  }
-  pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
-                                &shstrtab_[0], shstrtab_.size()));
-  for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
-    // Just add all the sections in induvidually since they are all over the
-    // place on the heap/stack.
-    Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
-    pieces.push_back(ElfFilePiece("section table piece", cur_off,
-                                  section_ptrs_[i], sizeof(Elf_Shdr)));
-  }
-
-  if (!WriteOutFile(pieces)) {
-    LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
-    return false;
-  }
-  // write out the actual oat file data.
-  Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
-  if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
-    PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
-                << " for " << elf_file_->GetPath();
-    return false;
-  }
-  std::unique_ptr<BufferedOutputStream> output_stream(
-      new BufferedOutputStream(new FileOutputStream(elf_file_)));
-  if (!oat_writer_->Write(output_stream.get())) {
-    PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
-    return false;
-  }
-
-  return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
-  // TODO It would be nice if this checked for overlap.
-  for (auto it = pieces.begin(); it != pieces.end(); ++it) {
-    if (it->data_) {
-      if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
-        PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
-                    << it->offset_ << " for " << elf_file_->GetPath();
-        return false;
-      }
-      if (!elf_file_->WriteFully(it->data_, it->size_)) {
-        PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupDynamic() {
-  dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
-  dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, &dynsym_builder_.strtab_);
-  dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
-  dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupRequiredSymbols() {
-  dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
-                            rodata_builder_.size_, STB_GLOBAL, STT_OBJECT);
-  dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
-                            text_builder_.size_, STB_GLOBAL, STT_OBJECT);
-  dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.size_ - 4,
-                            true, 4, STB_GLOBAL, STT_OBJECT);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
-  if (tag == DT_NULL) {
-    return;
-  }
-  dynamics_.push_back({nullptr, tag, d_un});
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
-                                                      ElfSectionBuilder* section) {
-  if (tag == DT_NULL) {
-    return;
-  }
-  dynamics_.push_back({section, tag, d_un});
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Dyn> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::GetDynamics(Elf_Word strsz,
-                                                                    Elf_Word soname) {
-  std::vector<Elf_Dyn> ret;
-  for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
-    if (it->section_) {
-      // We are adding an address relative to a section.
-      ret.push_back(
-          {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
-    } else {
-      ret.push_back({it->tag_, {it->off_}});
-    }
-  }
-  ret.push_back({DT_STRSZ, {strsz}});
-  ret.push_back({DT_SONAME, {soname}});
-  ret.push_back({DT_NULL, {0}});
-  return ret;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Sym> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateSymtab() {
-  std::vector<Elf_Sym> ret;
-  Elf_Sym undef_sym;
-  memset(&undef_sym, 0, sizeof(undef_sym));
-  undef_sym.st_shndx = SHN_UNDEF;
-  ret.push_back(undef_sym);
-
-  for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
-    Elf_Sym sym;
-    memset(&sym, 0, sizeof(sym));
-    sym.st_name = it->name_idx_;
-    if (it->is_relative_) {
-      sym.st_value = it->addr_ + it->section_->section_.sh_offset;
-    } else {
-      sym.st_value = it->addr_;
-    }
-    sym.st_size = it->size_;
-    sym.st_other = it->other_;
-    sym.st_shndx = it->section_->section_index_;
-    sym.st_info = it->info_;
-
-    ret.push_back(sym);
-  }
-  return ret;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-std::string ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateStrtab() {
-  std::string tab;
-  tab += '\0';
-  for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
-    it->name_idx_ = tab.size();
-    tab += it->name_;
-    tab += '\0';
-  }
-  strtab_.section_.sh_size = tab.size();
-  return tab;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::AssignSectionStr(
-    ElfSectionBuilder* builder, std::string* strtab) {
-  builder->section_.sh_name = strtab->size();
-  *strtab += builder->name_;
-  *strtab += '\0';
-  if (debug_logging_) {
-    LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
-              << "to shstrtab at offset " << builder->section_.sh_name;
-  }
-}
-
-// from bionic
-static unsigned elfhash(const char *_name) {
-  const unsigned char *name = (const unsigned char *) _name;
-  unsigned h = 0, g;
-
-  while (*name) {
-    h = (h << 4) + *name++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
-  }
-  return h;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-std::vector<Elf_Word> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateHashContents() {
-  // Here is how The ELF hash table works.
-  // There are 3 arrays to worry about.
-  // * The symbol table where the symbol information is.
-  // * The bucket array which is an array of indexes into the symtab and chain.
-  // * The chain array which is also an array of indexes into the symtab and chain.
-  //
-  // Lets say the state is something like this.
-  // +--------+       +--------+      +-----------+
-  // | symtab |       | bucket |      |   chain   |
-  // |  null  |       | 1      |      | STN_UNDEF |
-  // | <sym1> |       | 4      |      | 2         |
-  // | <sym2> |       |        |      | 5         |
-  // | <sym3> |       |        |      | STN_UNDEF |
-  // | <sym4> |       |        |      | 3         |
-  // | <sym5> |       |        |      | STN_UNDEF |
-  // +--------+       +--------+      +-----------+
-  //
-  // The lookup process (in python psudocode) is
-  //
-  // def GetSym(name):
-  //     # NB STN_UNDEF == 0
-  //     indx = bucket[elfhash(name) % num_buckets]
-  //     while indx != STN_UNDEF:
-  //         if GetSymbolName(symtab[indx]) == name:
-  //             return symtab[indx]
-  //         indx = chain[indx]
-  //     return SYMBOL_NOT_FOUND
-  //
-  // Between bucket and chain arrays every symtab index must be present exactly
-  // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
-  // Select number of buckets.
-  // This is essentially arbitrary.
-  Elf_Word nbuckets;
-  Elf_Word chain_size = GetSize();
-  if (symbols_.size() < 8) {
-    nbuckets = 2;
-  } else if (symbols_.size() < 32) {
-    nbuckets = 4;
-  } else if (symbols_.size() < 256) {
-    nbuckets = 16;
-  } else {
-    // Have about 32 ids per bucket.
-    nbuckets = RoundUp(symbols_.size()/32, 2);
-  }
-  std::vector<Elf_Word> hash;
-  hash.push_back(nbuckets);
-  hash.push_back(chain_size);
-  uint32_t bucket_offset = hash.size();
-  uint32_t chain_offset = bucket_offset + nbuckets;
-  hash.resize(hash.size() + nbuckets + chain_size, 0);
-
-  Elf_Word* buckets = hash.data() + bucket_offset;
-  Elf_Word* chain   = hash.data() + chain_offset;
-
-  // Set up the actual hash table.
-  for (Elf_Word i = 0; i < symbols_.size(); i++) {
-    // Add 1 since we need to have the null symbol that is not in the symbols
-    // list.
-    Elf_Word index = i + 1;
-    Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
-    if (buckets[hash_val] == 0) {
-      buckets[hash_val] = index;
-    } else {
-      hash_val = buckets[hash_val];
-      CHECK_LT(hash_val, chain_size);
-      while (chain[hash_val] != 0) {
-        hash_val = chain[hash_val];
-        CHECK_LT(hash_val, chain_size);
-      }
-      chain[hash_val] = index;
-      // Check for loops. Works because if this is non-empty then there must be
-      // another cell which already contains the same symbol index as this one,
-      // which means some symbol has more then one name, which isn't allowed.
-      CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
-    }
-  }
-
-  return hash;
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupEhdr() {
-  memset(&elf_header_, 0, sizeof(elf_header_));
-  elf_header_.e_ident[EI_MAG0]       = ELFMAG0;
-  elf_header_.e_ident[EI_MAG1]       = ELFMAG1;
-  elf_header_.e_ident[EI_MAG2]       = ELFMAG2;
-  elf_header_.e_ident[EI_MAG3]       = ELFMAG3;
-  elf_header_.e_ident[EI_CLASS]      = ELFCLASS32;
-  elf_header_.e_ident[EI_DATA]       = ELFDATA2LSB;
-  elf_header_.e_ident[EI_VERSION]    = EV_CURRENT;
-  elf_header_.e_ident[EI_OSABI]      = ELFOSABI_LINUX;
-  elf_header_.e_ident[EI_ABIVERSION] = 0;
-  elf_header_.e_type = ET_DYN;
-  elf_header_.e_version = 1;
-  elf_header_.e_entry = 0;
-  elf_header_.e_ehsize = sizeof(Elf_Ehdr);
-  elf_header_.e_phentsize = sizeof(Elf_Phdr);
-  elf_header_.e_shentsize = sizeof(Elf_Shdr);
-  elf_header_.e_phoff = sizeof(Elf_Ehdr);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetISA(InstructionSet isa) {
-  switch (isa) {
-    case kArm:
-      // Fall through.
-    case kThumb2: {
-      elf_header_.e_machine = EM_ARM;
-      elf_header_.e_flags = EF_ARM_EABI_VER5;
-      break;
-    }
-    case kArm64: {
-      elf_header_.e_machine = EM_AARCH64;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kX86: {
-      elf_header_.e_machine = EM_386;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kX86_64: {
-      elf_header_.e_machine = EM_X86_64;
-      elf_header_.e_flags = 0;
-      break;
-    }
-    case kMips: {
-      elf_header_.e_machine = EM_MIPS;
-      elf_header_.e_flags = (EF_MIPS_NOREORDER |
-                             EF_MIPS_PIC       |
-                             EF_MIPS_CPIC      |
-                             EF_MIPS_ABI_O32   |
-                             EF_MIPS_ARCH_32R2);
-      break;
-    }
-    default: {
-      fatal_error_ = true;
-      LOG(FATAL) << "Unknown instruction set: " << isa;
-      break;
-    }
-  }
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::AddSymbol(
-    const std::string& name, const ElfSectionBuilder* section, Elf_Addr addr,
-    bool is_relative, Elf_Word size, uint8_t binding, uint8_t type, uint8_t other) {
-  CHECK(section);
-  ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
-                                          MakeStInfo(binding, type), other, 0};
-  symbols_.push_back(state);
-}
-
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
   Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Create(File* elf_file,
                             OatWriter* oat_writer,
                             const std::vector<const DexFile*>& dex_files,
@@ -913,17 +85,14 @@
   return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
 }
 
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
 // Add patch information to this section. Each patch is a Elf_Word that
 // identifies an offset from the start of the text section
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
+static void ReservePatchSpace(const CompilerDriver* compiler_driver, std::vector<uint8_t>* buffer,
+                              bool debug) {
   size_t size =
-      compiler_driver_->GetCodeToPatch().size() +
-      compiler_driver_->GetMethodsToPatch().size() +
-      compiler_driver_->GetClassesToPatch().size();
+      compiler_driver->GetCodeToPatch().size() +
+      compiler_driver->GetMethodsToPatch().size() +
+      compiler_driver->GetClassesToPatch().size();
   if (size == 0) {
     if (debug) {
       LOG(INFO) << "No patches to record";
@@ -1046,6 +215,25 @@
   }
 }
 
+class OatWriterWrapper : public CodeOutput {
+ public:
+  explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+  bool Write(OutputStream* out) OVERRIDE {
+    return oat_writer_->Write(out);
+  }
+ private:
+  OatWriter* oat_writer_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+                              ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                                         Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+                              OatWriter* oat_writer);
+
 template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
           typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
           typename Elf_Phdr, typename Elf_Shdr>
@@ -1059,145 +247,40 @@
   Elf_Word oat_data_size = oat_header.GetExecutableOffset();
   uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
 
-  std::unique_ptr<ElfBuilder> builder(new ElfBuilder(
-      oat_writer,
-      elf_file_,
-      compiler_driver_->GetInstructionSet(),
-      0,
-      oat_data_size,
-      oat_data_size,
-      oat_exec_size,
-      compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
-      debug));
+  OatWriterWrapper wrapper(oat_writer);
+
+  std::unique_ptr<ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                             Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> > builder(
+      new ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                     Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>(
+          &wrapper,
+          elf_file_,
+          compiler_driver_->GetInstructionSet(),
+          0,
+          oat_data_size,
+          oat_data_size,
+          oat_exec_size,
+          compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
+          debug));
 
   if (!builder->Init()) {
     return false;
   }
 
   if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
-    WriteDebugSymbols(builder.get(), oat_writer);
+    WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
   }
 
   if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
-    ElfRawSectionBuilder oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0,
-                                     sizeof(uintptr_t), sizeof(uintptr_t));
-    ReservePatchSpace(oat_patches.GetBuffer(), debug);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
+        ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
+    ReservePatchSpace(compiler_driver_, oat_patches.GetBuffer(), debug);
     builder->RegisterRawSection(oat_patches);
   }
 
   return builder->Write();
 }
 
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer) {
-  std::unique_ptr<std::vector<uint8_t>> cfi_info(
-      ConstructCIEFrame(compiler_driver_->GetInstructionSet()));
-
-  Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
-
-  // Iterate over the compiled methods.
-  const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
-  ElfSymtabBuilder* symtab = &builder->symtab_builder_;
-  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
-    symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
-                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
-
-    // Include CFI for compiled method, if possible.
-    if (cfi_info.get() != nullptr) {
-      DCHECK(it->compiled_method_ != nullptr);
-
-      // Copy in the FDE, if present
-      const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
-      if (fde != nullptr) {
-        // Copy the information into cfi_info and then fix the address in the new copy.
-        int cur_offset = cfi_info->size();
-        cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
-        bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
-
-        // Set the 'CIE_pointer' field.
-        uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
-        uint64_t offset_to_update = CIE_pointer;
-        if (is_64bit) {
-          (*cfi_info)[offset_to_update+0] = CIE_pointer;
-          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
-          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
-          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
-          (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
-          (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
-          (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
-          (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
-        } else {
-          (*cfi_info)[offset_to_update+0] = CIE_pointer;
-          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
-          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
-          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
-        }
-
-        // Set the 'initial_location' field.
-        offset_to_update += is_64bit ? 8 : 4;
-        if (is_64bit) {
-          const uint64_t quick_code_start = it->low_pc_ + text_section_address;
-          (*cfi_info)[offset_to_update+0] = quick_code_start;
-          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
-          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
-          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
-          (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
-          (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
-          (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
-          (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
-        } else {
-          const uint32_t quick_code_start = it->low_pc_ + text_section_address;
-          (*cfi_info)[offset_to_update+0] = quick_code_start;
-          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
-          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
-          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
-        }
-      }
-    }
-  }
-
-  bool hasCFI = (cfi_info.get() != nullptr);
-  bool hasLineInfo = false;
-  for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
-    if (dbg_info.dbgstream_ != nullptr &&
-        !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
-      hasLineInfo = true;
-      break;
-    }
-  }
-
-  if (hasLineInfo || hasCFI) {
-    ElfRawSectionBuilder debug_info(".debug_info",     SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder debug_str(".debug_str",       SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-    ElfRawSectionBuilder debug_line(".debug_line",     SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-
-    FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
-                         debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
-                         hasLineInfo ? debug_line.GetBuffer() : nullptr,
-                         text_section_address);
-
-    builder->RegisterRawSection(debug_info);
-    builder->RegisterRawSection(debug_abbrev);
-
-    if (hasCFI) {
-      ElfRawSectionBuilder eh_frame(".eh_frame",  SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
-      eh_frame.SetBuffer(std::move(*cfi_info.get()));
-      builder->RegisterRawSection(eh_frame);
-    }
-
-    if (hasLineInfo) {
-      builder->RegisterRawSection(debug_line);
-    }
-
-    builder->RegisterRawSection(debug_str);
-  }
-}
-
 class LineTableGenerator FINAL : public Leb128Encoder {
  public:
   LineTableGenerator(int line_base, int line_range, int opcode_base,
@@ -1353,16 +436,19 @@
   }
 }
 
-template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
-          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
-          typename Elf_Phdr, typename Elf_Shdr>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
-  Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::FillInCFIInformation(OatWriter* oat_writer,
-                                          std::vector<uint8_t>* dbg_info,
-                                          std::vector<uint8_t>* dbg_abbrev,
-                                          std::vector<uint8_t>* dbg_str,
-                                          std::vector<uint8_t>* dbg_line,
-                                          uint32_t text_section_offset) {
+/*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+static void FillInCFIInformation(OatWriter* oat_writer,
+                                 std::vector<uint8_t>* dbg_info,
+                                 std::vector<uint8_t>* dbg_abbrev,
+                                 std::vector<uint8_t>* dbg_str,
+                                 std::vector<uint8_t>* dbg_line,
+                                 uint32_t text_section_offset) {
   const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
 
   uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
@@ -1591,6 +677,130 @@
   UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
 }
 
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+          typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+          typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+                              ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+                                         Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+                              OatWriter* oat_writer) {
+  std::unique_ptr<std::vector<uint8_t>> cfi_info(
+      ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+
+  Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
+
+  // Iterate over the compiled methods.
+  const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+  ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr,
+                   Elf_Sym, Elf_Shdr>* symtab = &builder->symtab_builder_;
+  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+    symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
+                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+    // Include CFI for compiled method, if possible.
+    if (cfi_info.get() != nullptr) {
+      DCHECK(it->compiled_method_ != nullptr);
+
+      // Copy in the FDE, if present
+      const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+      if (fde != nullptr) {
+        // Copy the information into cfi_info and then fix the address in the new copy.
+        int cur_offset = cfi_info->size();
+        cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+        bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
+
+        // Set the 'CIE_pointer' field.
+        uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
+        uint64_t offset_to_update = CIE_pointer;
+        if (is_64bit) {
+          (*cfi_info)[offset_to_update+0] = CIE_pointer;
+          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+          (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
+          (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
+          (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
+          (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
+        } else {
+          (*cfi_info)[offset_to_update+0] = CIE_pointer;
+          (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+          (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+          (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+        }
+
+        // Set the 'initial_location' field.
+        offset_to_update += is_64bit ? 8 : 4;
+        if (is_64bit) {
+          const uint64_t quick_code_start = it->low_pc_ + text_section_address;
+          (*cfi_info)[offset_to_update+0] = quick_code_start;
+          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+          (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
+          (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
+          (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
+          (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
+        } else {
+          const uint32_t quick_code_start = it->low_pc_ + text_section_address;
+          (*cfi_info)[offset_to_update+0] = quick_code_start;
+          (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+          (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+          (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+        }
+      }
+    }
+  }
+
+  bool hasCFI = (cfi_info.get() != nullptr);
+  bool hasLineInfo = false;
+  for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
+    if (dbg_info.dbgstream_ != nullptr &&
+        !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
+      hasLineInfo = true;
+      break;
+    }
+  }
+
+  if (hasLineInfo || hasCFI) {
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
+                                                                   SHT_PROGBITS,
+                                                                   0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_abbrev(".debug_abbrev",
+                                                                     SHT_PROGBITS,
+                                                                     0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_str(".debug_str",
+                                                                  SHT_PROGBITS,
+                                                                  0, nullptr, 0, 1, 0);
+    ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_line(".debug_line",
+                                                                   SHT_PROGBITS,
+                                                                   0, nullptr, 0, 1, 0);
+
+    FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+                         debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
+                         hasLineInfo ? debug_line.GetBuffer() : nullptr,
+                         text_section_address);
+
+    builder->RegisterRawSection(debug_info);
+    builder->RegisterRawSection(debug_abbrev);
+
+    if (hasCFI) {
+      ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+                                                                   SHT_PROGBITS,
+                                                                   SHF_ALLOC,
+                                                                   nullptr, 0, 4, 0);
+      eh_frame.SetBuffer(std::move(*cfi_info.get()));
+      builder->RegisterRawSection(eh_frame);
+    }
+
+    if (hasLineInfo) {
+      builder->RegisterRawSection(debug_line);
+    }
+
+    builder->RegisterRawSection(debug_str);
+  }
+}
+
 // Explicit instantiations
 template class ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
                               Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 890528e..4990ed0 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,7 +19,6 @@
 
 #include "elf_utils.h"
 #include "elf_writer.h"
-#include "instruction_set.h"
 
 namespace art {
 
@@ -50,300 +49,6 @@
     : ElfWriter(driver, elf_file) {}
   ~ElfWriterQuick() {}
 
-  class ElfBuilder;
-  void WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer);
-  void ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug);
-
-  class ElfSectionBuilder {
-   public:
-    ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
-                      const ElfSectionBuilder *link, Elf_Word info, Elf_Word align,
-                      Elf_Word entsize)
-        : name_(sec_name), link_(link) {
-      memset(&section_, 0, sizeof(section_));
-      section_.sh_type = type;
-      section_.sh_flags = flags;
-      section_.sh_info = info;
-      section_.sh_addralign = align;
-      section_.sh_entsize = entsize;
-    }
-
-    virtual ~ElfSectionBuilder() {}
-
-    Elf_Shdr section_;
-    Elf_Word section_index_ = 0;
-
-   protected:
-    const std::string name_;
-    const ElfSectionBuilder* link_;
-
-    Elf_Word GetLink() {
-      return (link_) ? link_->section_index_ : 0;
-    }
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfDynamicBuilder : public ElfSectionBuilder {
-   public:
-    void AddDynamicTag(Elf_Sword tag, Elf_Word d_un);
-    void AddDynamicTag(Elf_Sword tag, Elf_Word offset, ElfSectionBuilder* section);
-
-    ElfDynamicBuilder(const std::string& sec_name, ElfSectionBuilder *link)
-        : ElfSectionBuilder(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC, link,
-                            0, kPageSize, sizeof(Elf_Dyn)) {}
-    ~ElfDynamicBuilder() {}
-
-   protected:
-    struct ElfDynamicState {
-      ElfSectionBuilder* section_;
-      Elf_Sword tag_;
-      Elf_Word off_;
-    };
-    std::vector<ElfDynamicState> dynamics_;
-    Elf_Word GetSize() {
-      // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
-      // these must be added when we actually put the file together because
-      // their values are very dependent on state.
-      return dynamics_.size() + 3;
-    }
-
-    // Create the actual dynamic vector. strsz should be the size of the .dynstr
-    // table and soname_off should be the offset of the soname in .dynstr.
-    // Since niether can be found prior to final layout we will wait until here
-    // to add them.
-    std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname_off);
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfRawSectionBuilder : public ElfSectionBuilder {
-   public:
-    ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
-                         const ElfSectionBuilder* link, Elf_Word info, Elf_Word align,
-                         Elf_Word entsize)
-        : ElfSectionBuilder(sec_name, type, flags, link, info, align, entsize) {}
-    ~ElfRawSectionBuilder() {}
-    std::vector<uint8_t>* GetBuffer() { return &buf_; }
-    void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
-
-   protected:
-    std::vector<uint8_t> buf_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfOatSectionBuilder : public ElfSectionBuilder {
-   public:
-    ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
-                         Elf_Word type, Elf_Word flags)
-        : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
-          offset_(offset), size_(size) {}
-    ~ElfOatSectionBuilder() {}
-
-   protected:
-    // Offset of the content within the file.
-    Elf_Word offset_;
-    // Size of the content within the file.
-    Elf_Word size_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfSymtabBuilder : public ElfSectionBuilder {
-   public:
-    // Add a symbol with given name to this symtab. The symbol refers to
-    // 'relative_addr' within the given section and has the given attributes.
-    void AddSymbol(const std::string& name,
-                   const ElfSectionBuilder* section,
-                   Elf_Addr addr,
-                   bool is_relative,
-                   Elf_Word size,
-                   uint8_t binding,
-                   uint8_t type,
-                   uint8_t other = 0);
-
-    ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
-                     const std::string& str_name, Elf_Word str_type, bool alloc)
-        : ElfSectionBuilder(sec_name, type, ((alloc) ? SHF_ALLOC : 0U), &strtab_, 0,
-                            sizeof(Elf_Word), sizeof(Elf_Sym)),
-          str_name_(str_name), str_type_(str_type),
-          strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0U), NULL, 0, 1, 1) {}
-    ~ElfSymtabBuilder() {}
-
-   protected:
-    std::vector<Elf_Word> GenerateHashContents();
-    std::string GenerateStrtab();
-    std::vector<Elf_Sym> GenerateSymtab();
-
-    Elf_Word GetSize() {
-      // 1 is for the implicit NULL symbol.
-      return symbols_.size() + 1;
-    }
-
-    struct ElfSymbolState {
-      const std::string name_;
-      const ElfSectionBuilder* section_;
-      Elf_Addr addr_;
-      Elf_Word size_;
-      bool is_relative_;
-      uint8_t info_;
-      uint8_t other_;
-      // Used during Write() to temporarially hold name index in the strtab.
-      Elf_Word name_idx_;
-    };
-
-    // Information for the strsym for dynstr sections.
-    const std::string str_name_;
-    Elf_Word str_type_;
-    // The symbols in the same order they will be in the symbol table.
-    std::vector<ElfSymbolState> symbols_;
-    ElfSectionBuilder strtab_;
-
-   private:
-    friend class ElfBuilder;
-  };
-
-  class ElfBuilder FINAL {
-   public:
-    ElfBuilder(OatWriter* oat_writer,
-               File* elf_file,
-               InstructionSet isa,
-               Elf_Word rodata_relative_offset,
-               Elf_Word rodata_size,
-               Elf_Word text_relative_offset,
-               Elf_Word text_size,
-               const bool add_symbols,
-               bool debug = false)
-        : oat_writer_(oat_writer),
-          elf_file_(elf_file),
-          add_symbols_(add_symbols),
-          debug_logging_(debug),
-          text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
-                        SHF_ALLOC | SHF_EXECINSTR),
-          rodata_builder_(".rodata", rodata_size, rodata_relative_offset,
-                          SHT_PROGBITS, SHF_ALLOC),
-          dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
-          symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
-          hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0,
-                        sizeof(Elf_Word), sizeof(Elf_Word)),
-          dynamic_builder_(".dynamic", &dynsym_builder_),
-          shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
-      SetupEhdr();
-      SetupDynamic();
-      SetupRequiredSymbols();
-      SetISA(isa);
-    }
-    ~ElfBuilder() {}
-
-    bool Init();
-    bool Write();
-
-    // Adds the given raw section to the builder. This will copy it. The caller
-    // is responsible for deallocating their copy.
-    void RegisterRawSection(ElfRawSectionBuilder bld) {
-      other_builders_.push_back(bld);
-    }
-
-   private:
-    OatWriter* oat_writer_;
-    File* elf_file_;
-    const bool add_symbols_;
-    const bool debug_logging_;
-
-    bool fatal_error_ = false;
-
-    // What phdr is.
-    static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
-    enum : uint8_t {
-      PH_PHDR     = 0,
-      PH_LOAD_R__ = 1,
-      PH_LOAD_R_X = 2,
-      PH_LOAD_RW_ = 3,
-      PH_DYNAMIC  = 4,
-      PH_NUM      = 5,
-    };
-    static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
-    Elf_Phdr program_headers_[PH_NUM];
-
-    Elf_Ehdr elf_header_;
-
-    Elf_Shdr null_hdr_;
-    std::string shstrtab_;
-    uint32_t section_index_;
-    std::string dynstr_;
-    uint32_t dynstr_soname_offset_;
-    std::vector<Elf_Shdr*> section_ptrs_;
-    std::vector<Elf_Word> hash_;
-
-   public:
-    ElfOatSectionBuilder text_builder_;
-    ElfOatSectionBuilder rodata_builder_;
-    ElfSymtabBuilder dynsym_builder_;
-    ElfSymtabBuilder symtab_builder_;
-    ElfSectionBuilder hash_builder_;
-    ElfDynamicBuilder dynamic_builder_;
-    ElfSectionBuilder shstrtab_builder_;
-    std::vector<ElfRawSectionBuilder> other_builders_;
-
-   private:
-    void SetISA(InstructionSet isa);
-    void SetupEhdr();
-
-    // Sets up a bunch of the required Dynamic Section entries.
-    // Namely it will initialize all the mandatory ones that it can.
-    // Specifically:
-    // DT_HASH
-    // DT_STRTAB
-    // DT_SYMTAB
-    // DT_SYMENT
-    //
-    // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
-    void SetupDynamic();
-
-    // Sets up the basic dynamic symbols that are needed, namely all those we
-    // can know already.
-    //
-    // Specifically adds:
-    // oatdata
-    // oatexec
-    // oatlastword
-    void SetupRequiredSymbols();
-    void AssignSectionStr(ElfSectionBuilder *builder, std::string* strtab);
-    struct ElfFilePiece {
-      ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
-          : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
-      ~ElfFilePiece() {}
-
-      const std::string& dbg_name_;
-      Elf_Word offset_;
-      const void *data_;
-      Elf_Word size_;
-      static bool Compare(ElfFilePiece a, ElfFilePiece b) {
-        return a.offset_ < b.offset_;
-      }
-    };
-
-    // Write each of the pieces out to the file.
-    bool WriteOutFile(const std::vector<ElfFilePiece>& pieces);
-    bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
-  };
-
-  /*
-   * @brief Generate the DWARF debug_info and debug_abbrev sections
-   * @param oat_writer The Oat file Writer.
-   * @param dbg_info Compilation unit information.
-   * @param dbg_abbrev Abbreviations used to generate dbg_info.
-   * @param dbg_str Debug strings.
-   */
-  void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
-                            std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str,
-                            std::vector<uint8_t>* dbg_line, uint32_t text_section_offset);
-
   DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
 };
 
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 680ce0a..c5d1478 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -140,7 +140,7 @@
 
 struct OatWriter::GcMapDataAccess {
   static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
-    return &compiled_method->GetGcMap();
+    return compiled_method->GetGcMap();
   }
 
   static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -434,13 +434,15 @@
         } else {
           status = mirror::Class::kStatusNotReady;
         }
-        const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
-        size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
-        bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
-        CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
-            << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
-            << (status < mirror::Class::kStatusVerified) << " " << status << " "
-            << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+        std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+        if (gc_map != nullptr) {
+          size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
+          bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+          CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+              << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+              << (status < mirror::Class::kStatusVerified) << " " << status << " "
+              << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+        }
       }
 
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
@@ -475,7 +477,7 @@
       DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
 
       const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
-      uint32_t map_size = map->size() * sizeof((*map)[0]);
+      uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       if (map_size != 0u) {
         auto lb = dedupe_map_.lower_bound(map);
         if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) {
@@ -645,7 +647,7 @@
 
       // Write deduplicated map.
       const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
-      size_t map_size = map->size() * sizeof((*map)[0]);
+      size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       DCHECK((map_size == 0u && map_offset == 0u) ||
             (map_size != 0u && map_offset != 0u && map_offset <= offset_))
           << PrettyMethod(it.GetMemberIndex(), *dex_file_);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 43e6b83..ecd6802 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -413,6 +413,7 @@
     current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
         null_check,
         value,
+        field_type,
         resolved_field->GetOffset()));
   } else {
     current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
@@ -453,7 +454,8 @@
   if (is_put) {
     HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
     // TODO: Insert a type check node if the type is Object.
-    current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+    current_block_->AddInstruction(new (arena_) HArraySet(
+        object, index, value, anticipated_type, dex_offset));
   } else {
     current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
@@ -750,6 +752,13 @@
     ARRAY_XX(_CHAR, Primitive::kPrimChar);
     ARRAY_XX(_SHORT, Primitive::kPrimShort);
 
+    case Instruction::ARRAY_LENGTH: {
+      HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+      current_block_->AddInstruction(new (arena_) HArrayLength(object));
+      UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+      break;
+    }
+
     default:
       return false;
   }
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 7269fff..7731e6e 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,10 +41,11 @@
   if (!is_leaf) {
     MarkNotLeaf();
   }
-  ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
-                   + GetGraph()->GetNumberOfLocalVRegs()
-                   + GetGraph()->GetNumberOfTemporaries()
-                   + 1 /* filler */);
+  ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
+                     + GetGraph()->GetNumberOfTemporaries()
+                     + 1 /* filler */,
+                   GetGraph()->GetMaximumNumberOfOutVRegs()
+                     + 1 /* current method */);
   GenerateFrameEntry();
 
   for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
@@ -110,10 +111,10 @@
   return -1;
 }
 
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) {
+void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots, size_t number_of_out_slots) {
   SetFrameSize(RoundUp(
       number_of_spill_slots * kVRegSize
-      + kVRegSize  // Art method
+      + number_of_out_slots * kVRegSize
       + FrameEntrySpillSize(),
       kStackAlignment));
 }
@@ -376,4 +377,95 @@
   *data = vmap_encoder.GetData();
 }
 
+void CodeGenerator::BuildStackMaps(std::vector<uint8_t>* data) {
+  uint32_t size = stack_map_stream_.ComputeNeededSize();
+  data->resize(size);
+  MemoryRegion region(data->data(), size);
+  stack_map_stream_.FillIn(region);
+}
+
+void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+  // Collect PC infos for the mapping table.
+  struct PcInfo pc_info;
+  pc_info.dex_pc = dex_pc;
+  pc_info.native_pc = GetAssembler()->CodeSize();
+  pc_infos_.Add(pc_info);
+
+  // Populate stack map information.
+
+  if (instruction == nullptr) {
+    // For stack overflow checks.
+    stack_map_stream_.AddStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, 0);
+    return;
+  }
+
+  LocationSummary* locations = instruction->GetLocations();
+  HEnvironment* environment = instruction->GetEnvironment();
+
+  size_t environment_size = instruction->EnvironmentSize();
+
+  size_t register_mask = 0;
+  size_t inlining_depth = 0;
+  stack_map_stream_.AddStackMapEntry(
+      dex_pc, pc_info.native_pc, register_mask,
+      locations->GetStackMask(), environment_size, inlining_depth);
+
+  // Walk over the environment, and record the location of dex registers.
+  for (size_t i = 0; i < environment_size; ++i) {
+    HInstruction* current = environment->GetInstructionAt(i);
+    if (current == nullptr) {
+      stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kNone, 0);
+      continue;
+    }
+
+    Location location = locations->GetEnvironmentAt(i);
+    switch (location.GetKind()) {
+      case Location::kConstant: {
+        DCHECK(current == location.GetConstant());
+        if (current->IsLongConstant()) {
+          int64_t value = current->AsLongConstant()->GetValue();
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+          ++i;
+          DCHECK_LT(i, environment_size);
+        } else {
+          DCHECK(current->IsIntConstant());
+          int32_t value = current->AsIntConstant()->GetValue();
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+        }
+        break;
+      }
+
+      case Location::kStackSlot: {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+        break;
+      }
+
+      case Location::kDoubleStackSlot: {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack,
+                                              location.GetHighStackIndex(kVRegSize));
+        ++i;
+        DCHECK_LT(i, environment_size);
+        break;
+      }
+
+      case Location::kRegister : {
+        int id = location.reg().RegId();
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+        if (current->GetType() == Primitive::kPrimDouble
+            || current->GetType() == Primitive::kPrimLong) {
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+          ++i;
+          DCHECK_LT(i, environment_size);
+        }
+        break;
+      }
+
+      default:
+        LOG(FATAL) << "Unexpected kind " << location.GetKind();
+    }
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 12337c9..a83d703 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -23,6 +23,7 @@
 #include "locations.h"
 #include "memory_region.h"
 #include "nodes.h"
+#include "stack_map_stream.h"
 #include "utils/assembler.h"
 
 namespace art {
@@ -97,7 +98,7 @@
   virtual HGraphVisitor* GetInstructionVisitor() = 0;
   virtual Assembler* GetAssembler() = 0;
   virtual size_t GetWordSize() const = 0;
-  void ComputeFrameSize(size_t number_of_spill_slots);
+  void ComputeFrameSize(size_t number_of_spill_slots, size_t number_of_out_slots);
   virtual size_t FrameEntrySpillSize() const = 0;
   int32_t GetStackSlot(HLocal* local) const;
   Location GetTemporaryLocation(HTemporary* temp) const;
@@ -114,12 +115,7 @@
   virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
   virtual InstructionSet GetInstructionSet() const = 0;
 
-  void RecordPcInfo(uint32_t dex_pc) {
-    struct PcInfo pc_info;
-    pc_info.dex_pc = dex_pc;
-    pc_info.native_pc = GetAssembler()->CodeSize();
-    pc_infos_.Add(pc_info);
-  }
+  void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
 
   void AddSlowPath(SlowPathCode* slow_path) {
     slow_paths_.Add(slow_path);
@@ -131,6 +127,7 @@
   void BuildVMapTable(std::vector<uint8_t>* vector) const;
   void BuildNativeGCMap(
       std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+  void BuildStackMaps(std::vector<uint8_t>* vector);
 
   bool IsLeafMethod() const {
     return is_leaf_;
@@ -149,7 +146,8 @@
         pc_infos_(graph->GetArena(), 32),
         slow_paths_(graph->GetArena(), 8),
         blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
-        is_leaf_(true) {}
+        is_leaf_(true),
+        stack_map_stream_(graph->GetArena()) {}
   ~CodeGenerator() {}
 
   // Register allocation logic.
@@ -184,6 +182,8 @@
 
   bool is_leaf_;
 
+  StackMapStream stack_map_stream_;
+
   DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
 };
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..e72e39b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -61,18 +61,18 @@
 
 class NullCheckSlowPathARM : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
     __ ldr(LR, Address(TR, offset));
     __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
 };
 
@@ -92,10 +92,12 @@
 
 class BoundsCheckSlowPathARM : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
+  explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction,
                                   Location index_location,
                                   Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
@@ -106,11 +108,11 @@
     int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
     __ ldr(LR, Address(TR, offset));
     __ blx(LR);
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
@@ -277,7 +279,7 @@
     } else {
       __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
       __ ldr(IP, Address(IP, 0));
-      RecordPcInfo(0);
+      RecordPcInfo(nullptr, 0);
     }
   }
 
@@ -545,14 +547,14 @@
 }
 
 void LocationsBuilderARM::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
@@ -595,13 +597,13 @@
 
 
 void LocationsBuilderARM::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
@@ -695,7 +697,8 @@
 }
 
 void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -713,25 +716,24 @@
     default:
       LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
 }
 
 void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
@@ -747,7 +749,8 @@
 }
 
 void LocationsBuilderARM::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -766,8 +769,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
@@ -794,8 +795,8 @@
 }
 
 void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
   locations->AddTemp(ArmCoreLocation(R0));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -826,8 +827,6 @@
       LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
       break;
   }
-
-  invoke->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
@@ -859,12 +858,13 @@
   // LR()
   __ blx(LR);
 
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderARM::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -884,7 +884,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
@@ -924,7 +923,8 @@
 }
 
 void LocationsBuilderARM::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -944,7 +944,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
@@ -985,13 +984,12 @@
 }
 
 void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
   locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
   locations->SetOut(ArmCoreLocation(R0));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
@@ -1003,12 +1001,13 @@
   __ ldr(LR, Address(TR, offset));
   __ blx(LR);
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -1016,7 +1015,6 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
@@ -1024,10 +1022,10 @@
 }
 
 void LocationsBuilderARM::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
@@ -1037,11 +1035,11 @@
 }
 
 void LocationsBuilderARM::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
@@ -1081,12 +1079,12 @@
 }
 
 void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
@@ -1094,22 +1092,22 @@
 }
 
 void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (instruction->GetFieldType() == Primitive::kPrimNot) {
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -1154,10 +1152,10 @@
 }
 
 void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1214,16 +1212,15 @@
 }
 
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1237,11 +1234,11 @@
 }
 
 void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
@@ -1340,27 +1337,27 @@
 }
 
 void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
-  if (value_type == Primitive::kPrimNot) {
+  Primitive::Type value_type = instruction->GetComponentType();
+  bool is_object = value_type == Primitive::kPrimNot;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+  if (is_object) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
     locations->SetInAt(2, Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsArm().AsCoreRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1408,7 +1405,7 @@
       int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
       __ ldr(LR, Address(TR, offset));
       __ blx(LR);
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       DCHECK(!codegen_->IsLeafMethod());
       break;
     }
@@ -1436,10 +1433,10 @@
 }
 
 void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
@@ -1451,18 +1448,18 @@
 }
 
 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   Register index = locations->InAt(0).AsArm().AsCoreRegister();
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 35b8116..6602d3f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -61,16 +61,16 @@
 
 class NullCheckSlowPathX86 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowNullPointer)));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
 };
 
@@ -91,10 +91,10 @@
 
 class BoundsCheckSlowPathX86 : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
+  explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction,
                                   Location index_location,
                                   Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+      : instruction_(instruction), index_location_(index_location), length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
@@ -103,11 +103,11 @@
     x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
     x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
@@ -244,7 +244,7 @@
   bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
   if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
     __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
-    RecordPcInfo(0);
+    RecordPcInfo(nullptr, 0);
   }
 
   // The return PC has already been pushed on the stack.
@@ -398,6 +398,7 @@
       __ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)));
     }
   } else {
+    DCHECK(destination.IsDoubleStackSlot());
     if (source.IsRegister()) {
       __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow());
       __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
@@ -503,14 +504,14 @@
 }
 
 void LocationsBuilderX86::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
@@ -564,7 +565,8 @@
 }
 
 void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -589,13 +591,13 @@
 }
 
 void LocationsBuilderX86::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
@@ -665,18 +667,18 @@
 }
 
 void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) {
@@ -693,7 +695,8 @@
 }
 
 void LocationsBuilderX86::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -712,7 +715,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
@@ -740,8 +742,8 @@
 }
 
 void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
   locations->AddTemp(X86CpuLocation(EAX));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -799,11 +801,12 @@
   __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
 void LocationsBuilderX86::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -823,7 +826,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
@@ -876,7 +878,8 @@
 }
 
 void LocationsBuilderX86::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -896,7 +899,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
@@ -949,13 +951,12 @@
 }
 
 void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   locations->SetOut(X86CpuLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
   locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
@@ -966,12 +967,13 @@
   __ fs()->call(
       Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
 
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
 }
 
 void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -979,17 +981,16 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
 }
 
 void LocationsBuilderX86::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
@@ -1000,11 +1001,11 @@
 }
 
 void LocationsBuilderX86::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
@@ -1050,12 +1051,12 @@
 }
 
 void LocationsBuilderX86::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
@@ -1063,9 +1064,10 @@
 }
 
 void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
   if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) {
     // Ensure the value is in a byte register.
     locations->SetInAt(1, X86CpuLocation(EAX));
@@ -1078,14 +1080,13 @@
     // Ensure the card is in a byte register.
     locations->AddTemp(X86CpuLocation(ECX));
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsX86().AsCpuRegister();
   uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -1144,10 +1145,10 @@
 }
 
 void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1205,16 +1206,15 @@
 }
 
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::Any());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1231,11 +1231,11 @@
 }
 
 void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
@@ -1331,14 +1331,16 @@
 }
 
 void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction,
+      value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall);
+
   if (value_type == Primitive::kPrimNot) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -1349,15 +1351,13 @@
       locations->SetInAt(2, Location::RequiresRegister());
     }
   }
-
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   Register obj = locations->InAt(0).AsX86().AsCpuRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1401,7 +1401,7 @@
     case Primitive::kPrimNot: {
       DCHECK(!codegen_->IsLeafMethod());
       __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       break;
     }
 
@@ -1446,18 +1446,18 @@
 }
 
 void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   Register index = locations->InAt(0).AsX86().AsCpuRegister();
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c4571ca..b2d81e3 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -65,17 +65,17 @@
 
 class NullCheckSlowPathX86_64 : public SlowPathCode {
  public:
-  explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+  explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     __ Bind(GetEntryLabel());
     __ gs()->call(
         Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HNullCheck* const instruction_;
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
 };
 
@@ -97,10 +97,12 @@
 
 class BoundsCheckSlowPathX86_64 : public SlowPathCode {
  public:
-  explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
+  explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction,
                                      Location index_location,
                                      Location length_location)
-      : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
 
   virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
@@ -110,11 +112,11 @@
     x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
     __ gs()->call(Address::Absolute(
         QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
-    codegen->RecordPcInfo(dex_pc_);
+    codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
   }
 
  private:
-  const uint32_t dex_pc_;
+  HBoundsCheck* const instruction_;
   const Location index_location_;
   const Location length_location_;
 
@@ -214,7 +216,7 @@
   if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
     __ testq(CpuRegister(RAX), Address(
         CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
-    RecordPcInfo(0);
+    RecordPcInfo(nullptr, 0);
   }
 
   // The return PC has already been pushed on the stack.
@@ -385,14 +387,14 @@
 }
 
 void LocationsBuilderX86_64::VisitIf(HIf* if_instr) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
   HInstruction* cond = if_instr->InputAt(0);
   DCHECK(cond->IsCondition());
   HCondition* condition = cond->AsCondition();
   if (condition->NeedsMaterialization()) {
     locations->SetInAt(0, Location::Any());
   }
-  if_instr->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
@@ -444,7 +446,8 @@
 }
 
 void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
   switch (store->InputAt(1)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -462,20 +465,19 @@
     default:
       LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
   }
-  store->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
 }
 
 void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   if (comp->NeedsMaterialization()) {
     locations->SetOut(Location::RequiresRegister());
   }
-  comp->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
@@ -545,11 +547,11 @@
 }
 
 void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  compare->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
@@ -578,18 +580,18 @@
 }
 
 void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
 }
 
 void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
   locations->SetOut(Location::ConstantLocation(constant));
-  constant->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
@@ -605,7 +607,8 @@
 }
 
 void LocationsBuilderX86_64::VisitReturn(HReturn* ret) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
   switch (ret->InputAt(0)->GetType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -620,7 +623,6 @@
     default:
       LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
   }
-  ret->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
@@ -686,8 +688,8 @@
 }
 
 void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
   locations->AddTemp(X86_64CpuLocation(RDI));
 
   InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -715,8 +717,6 @@
       LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
       break;
   }
-
-  invoke->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
@@ -742,11 +742,12 @@
   __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke->GetDexPc());
+  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
 }
 
 void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
@@ -771,7 +772,6 @@
     default:
       LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
   }
-  add->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
@@ -812,7 +812,8 @@
 }
 
 void LocationsBuilderX86_64::VisitSub(HSub* sub) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
@@ -837,7 +838,6 @@
     default:
       LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
   }
-  sub->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
@@ -878,10 +878,9 @@
 }
 
 void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
-  codegen_->MarkNotLeaf();
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   locations->SetOut(X86_64CpuLocation(RAX));
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
@@ -893,11 +892,12 @@
       QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
 
   DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(instruction->GetDexPc());
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
 }
 
 void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
   if (location.IsStackSlot()) {
     location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -905,7 +905,6 @@
     location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
   }
   locations->SetOut(location);
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
@@ -913,10 +912,10 @@
 }
 
 void LocationsBuilderX86_64::VisitNot(HNot* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) {
@@ -927,12 +926,12 @@
 }
 
 void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     locations->SetInAt(i, Location::Any());
   }
   locations->SetOut(Location::Any());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) {
@@ -940,15 +939,15 @@
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Temporary registers for the write barrier.
-  if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+  if (instruction->GetFieldType() == Primitive::kPrimNot) {
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -956,7 +955,7 @@
   CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
   CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister();
   size_t offset = instruction->GetFieldOffset().SizeValue();
-  Primitive::Type field_type = instruction->InputAt(1)->GetType();
+  Primitive::Type field_type = instruction->GetFieldType();
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
@@ -997,10 +996,10 @@
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1051,16 +1050,15 @@
 }
 
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::Any());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc());
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
@@ -1077,11 +1075,11 @@
 }
 
 void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -1174,27 +1172,27 @@
 }
 
 void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
-  if (value_type == Primitive::kPrimNot) {
+  Primitive::Type value_type = instruction->GetComponentType();
+  bool is_object = value_type == Primitive::kPrimNot;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+      instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+  if (is_object) {
     InvokeRuntimeCallingConvention calling_convention;
     locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
     locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
     locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
-    codegen_->MarkNotLeaf();
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
     locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
     locations->SetInAt(2, Location::RequiresRegister());
   }
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
   Location index = locations->InAt(1);
-  Primitive::Type value_type = instruction->InputAt(2)->GetType();
+  Primitive::Type value_type = instruction->GetComponentType();
 
   switch (value_type) {
     case Primitive::kPrimBoolean:
@@ -1238,7 +1236,7 @@
     case Primitive::kPrimNot: {
       __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
       DCHECK(!codegen_->IsLeafMethod());
-      codegen_->RecordPcInfo(instruction->GetDexPc());
+      codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
       break;
     }
 
@@ -1264,10 +1262,10 @@
 }
 
 void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
@@ -1279,18 +1277,18 @@
 }
 
 void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // TODO: Have a normalization phase that makes this instruction never used.
   locations->SetOut(Location::SameAsFirstInput());
-  instruction->SetLocations(locations);
 }
 
 void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
   LocationSummary* locations = instruction->GetLocations();
   SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
-      instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 468cfb7..fce97bd 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,13 +20,29 @@
 
 namespace art {
 
-LocationSummary::LocationSummary(HInstruction* instruction)
+LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
     : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
-      temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
+      temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
+      environment_(instruction->GetBlock()->GetGraph()->GetArena(),
+                   instruction->EnvironmentSize()),
+      call_kind_(call_kind),
+      stack_mask_(nullptr),
+      register_mask_(0),
+      live_registers_(0) {
   inputs_.SetSize(instruction->InputCount());
-  for (size_t i = 0; i < instruction->InputCount(); i++) {
+  for (size_t i = 0; i < instruction->InputCount(); ++i) {
     inputs_.Put(i, Location());
   }
+  environment_.SetSize(instruction->EnvironmentSize());
+  for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) {
+    environment_.Put(i, Location());
+  }
+  instruction->SetLocations(this);
+
+  if (NeedsSafepoint()) {
+    ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena();
+    stack_mask_ = new (arena) ArenaBitVector(arena, 0, true);
+  }
 }
 
 
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index aaddb09..041e85b 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
 
 #include "base/bit_field.h"
+#include "base/bit_vector.h"
 #include "utils/allocation.h"
 #include "utils/growable_array.h"
 #include "utils/managed_register.h"
@@ -43,13 +44,13 @@
     // low bits are in the last parameter register, and the high
     // bits are in a stack slot. The kQuickParameter kind is for
     // handling this special case.
-    kQuickParameter = 5,
+    kQuickParameter = 6,
 
     // Unallocated location represents a location that is not fixed and can be
     // allocated by a register allocator.  Each unallocated location has
     // a policy that specifies what kind of location is suitable. Payload
     // contains register allocation policy.
-    kUnallocated = 6,
+    kUnallocated = 7,
   };
 
   Location() : value_(kInvalid) {
@@ -59,8 +60,8 @@
     COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
+    COMPILE_ASSERT((kQuickParameter & kLocationTagMask) != kConstant, TagError);
     COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
-    COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
 
     DCHECK(!IsValid());
   }
@@ -173,7 +174,7 @@
   x86_64::X86_64ManagedRegister AsX86_64() const;
 
   Kind GetKind() const {
-    return KindField::Decode(value_);
+    return IsConstant() ? kConstant : KindField::Decode(value_);
   }
 
   bool Equals(Location other) const {
@@ -275,7 +276,13 @@
  */
 class LocationSummary : public ArenaObject {
  public:
-  explicit LocationSummary(HInstruction* instruction);
+  enum CallKind {
+    kNoCall,
+    kCallOnSlowPath,
+    kCall
+  };
+
+  explicit LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
 
   void SetInAt(uint32_t at, Location location) {
     inputs_.Put(at, location);
@@ -309,12 +316,50 @@
     return temps_.Size();
   }
 
+  void SetEnvironmentAt(uint32_t at, Location location) {
+    environment_.Put(at, location);
+  }
+
+  Location GetEnvironmentAt(uint32_t at) const {
+    return environment_.Get(at);
+  }
+
   Location Out() const { return output_; }
 
+  bool CanCall() const { return call_kind_ != kNoCall; }
+  bool NeedsSafepoint() const { return CanCall(); }
+
+  void SetStackBit(uint32_t index) {
+    stack_mask_->SetBit(index);
+  }
+
+  void SetRegisterBit(uint32_t reg_id) {
+    register_mask_ |= (1 << reg_id);
+  }
+
+  void SetLiveRegister(uint32_t reg_id) {
+    live_registers_ |= (1 << reg_id);
+  }
+
+  BitVector* GetStackMask() const {
+    return stack_mask_;
+  }
+
  private:
   GrowableArray<Location> inputs_;
   GrowableArray<Location> temps_;
+  GrowableArray<Location> environment_;
   Location output_;
+  const CallKind call_kind_;
+
+  // Mask of objects that live in the stack.
+  BitVector* stack_mask_;
+
+  // Mask of objects that live in register.
+  uint32_t register_mask_;
+
+  // Registers that are in use at this position.
+  uint32_t live_registers_;
 
   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
 };
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 490d345..207c605 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -414,6 +414,10 @@
   env_uses_ = nullptr;
 }
 
+size_t HInstruction::EnvironmentSize() const {
+  return HasEnvironment() ? environment_->Size() : 0;
+}
+
 void HPhi::AddInput(HInstruction* input) {
   DCHECK(input->GetBlock() != nullptr);
   inputs_.Add(input);
@@ -464,4 +468,16 @@
   return false;
 }
 
+bool HInstruction::Equals(HInstruction* other) const {
+  if (!InstructionTypeEquals(other)) return false;
+  if (!InstructionDataEquals(other)) return false;
+  if (GetType() != other->GetType()) return false;
+  if (InputCount() != other->InputCount()) return false;
+
+  for (size_t i = 0, e = InputCount(); i < e; ++i) {
+    if (InputAt(i) != other->InputAt(i)) return false;
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index bb699e4..9018fee 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -455,6 +455,9 @@
 #define DECLARE_INSTRUCTION(type)                          \
   virtual const char* DebugName() const { return #type; }  \
   virtual H##type* As##type() { return this; }             \
+  virtual bool InstructionTypeEquals(HInstruction* other) const {     \
+    return other->Is##type();                              \
+  }                                                        \
   virtual void Accept(HGraphVisitor* visitor)              \
 
 template <typename T>
@@ -477,9 +480,42 @@
   DISALLOW_COPY_AND_ASSIGN(HUseListNode);
 };
 
+// Represents the side effects an instruction may have.
+class SideEffects : public ValueObject {
+ public:
+  static SideEffects None() {
+    return SideEffects(0);
+  }
+
+  static SideEffects All() {
+    return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_);
+  }
+
+  static SideEffects ChangesSomething() {
+    return SideEffects((1 << kFlagChangesCount) - 1);
+  }
+
+  static SideEffects DependsOnSomething() {
+    int count = kFlagDependsOnCount - kFlagChangesCount;
+    return SideEffects(((1 << count) - 1) << kFlagChangesCount);
+  }
+
+ private:
+  static constexpr int kFlagChangesSomething = 0;
+  static constexpr int kFlagChangesCount = kFlagChangesSomething + 1;
+
+  static constexpr int kFlagDependsOnSomething = kFlagChangesCount;
+  static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1;
+
+ private:
+  explicit SideEffects(size_t flags) : flags_(flags) {}
+
+  const size_t flags_;
+};
+
 class HInstruction : public ArenaObject {
  public:
-  HInstruction()
+  explicit HInstruction(SideEffects side_effects)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -490,7 +526,8 @@
         environment_(nullptr),
         locations_(nullptr),
         live_interval_(nullptr),
-        lifetime_position_(kNoLifetime) {}
+        lifetime_position_(kNoLifetime),
+        side_effects_(side_effects) {}
 
   virtual ~HInstruction() {}
 
@@ -554,6 +591,10 @@
   HEnvironment* GetEnvironment() const { return environment_; }
   void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
 
+  // Returns the number of entries in the environment. Typically, that is the
+  // number of dex registers in a method. It could be more in case of inlining.
+  size_t EnvironmentSize() const;
+
   LocationSummary* GetLocations() const { return locations_; }
   void SetLocations(LocationSummary* locations) { locations_ = locations; }
 
@@ -570,6 +611,22 @@
   FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
 #undef INSTRUCTION_TYPE_CHECK
 
+  // Returns whether the instruction can be moved within the graph.
+  virtual bool CanBeMoved() const { return false; }
+
+  // Returns whether the two instructions are of the same kind.
+  virtual bool InstructionTypeEquals(HInstruction* other) const { return false; }
+
+  // Returns whether any data encoded in the two instructions is equal.
+  // This method does not look at the inputs. Both instructions must be
+  // of the same type, otherwise the method has undefined behavior.
+  virtual bool InstructionDataEquals(HInstruction* other) const { return false; }
+
+  // Returns whether two instructions are equal, that is:
+  // 1) They have the same type and contain the same data,
+  // 2) Their inputs are identical.
+  bool Equals(HInstruction* other) const;
+
   size_t GetLifetimePosition() const { return lifetime_position_; }
   void SetLifetimePosition(size_t position) { lifetime_position_ = position; }
   LiveInterval* GetLiveInterval() const { return live_interval_; }
@@ -583,7 +640,7 @@
 
   // An instruction gets an id when it is added to the graph.
   // It reflects creation order. A negative id means the instruction
-  // has not beed added to the graph.
+  // has not been added to the graph.
   int id_;
 
   // When doing liveness analysis, instructions that have uses get an SSA index.
@@ -595,6 +652,8 @@
   // List of environments that contain this instruction.
   HUseListNode<HEnvironment>* env_uses_;
 
+  // The environment associated with this instruction. Not null if the instruction
+  // might jump out of the method.
   HEnvironment* environment_;
 
   // Set by the code generator.
@@ -607,6 +666,8 @@
   // order of blocks where this instruction's live interval start.
   size_t lifetime_position_;
 
+  const SideEffects side_effects_;
+
   friend class HBasicBlock;
   friend class HInstructionList;
 
@@ -660,10 +721,16 @@
     vregs_.Put(index, instruction);
   }
 
+  HInstruction* GetInstructionAt(size_t index) const {
+    return vregs_.Get(index);
+  }
+
   GrowableArray<HInstruction*>* GetVRegs() {
     return &vregs_;
   }
 
+  size_t Size() const { return vregs_.Size(); }
+
  private:
   GrowableArray<HInstruction*> vregs_;
 
@@ -777,7 +844,8 @@
 template<intptr_t N>
 class HTemplateInstruction: public HInstruction {
  public:
-  HTemplateInstruction<N>() : inputs_() {}
+  HTemplateInstruction<N>(SideEffects side_effects)
+      : HInstruction(side_effects), inputs_() {}
   virtual ~HTemplateInstruction() {}
 
   virtual size_t InputCount() const { return N; }
@@ -795,9 +863,10 @@
 };
 
 template<intptr_t N>
-class HExpression: public HTemplateInstruction<N> {
+class HExpression : public HTemplateInstruction<N> {
  public:
-  explicit HExpression<N>(Primitive::Type type) : type_(type) {}
+  HExpression<N>(Primitive::Type type, SideEffects side_effects)
+      : HTemplateInstruction<N>(side_effects), type_(type) {}
   virtual ~HExpression() {}
 
   virtual Primitive::Type GetType() const { return type_; }
@@ -810,7 +879,7 @@
 // instruction that branches to the exit block.
 class HReturnVoid : public HTemplateInstruction<0> {
  public:
-  HReturnVoid() {}
+  HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
 
   virtual bool IsControlFlow() const { return true; }
 
@@ -824,7 +893,7 @@
 // instruction that branches to the exit block.
 class HReturn : public HTemplateInstruction<1> {
  public:
-  explicit HReturn(HInstruction* value) {
+  explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, value);
   }
 
@@ -841,7 +910,7 @@
 // exit block.
 class HExit : public HTemplateInstruction<0> {
  public:
-  HExit() {}
+  HExit() : HTemplateInstruction(SideEffects::None()) {}
 
   virtual bool IsControlFlow() const { return true; }
 
@@ -854,14 +923,14 @@
 // Jumps from one block to another.
 class HGoto : public HTemplateInstruction<0> {
  public:
-  HGoto() {}
+  HGoto() : HTemplateInstruction(SideEffects::None()) {}
+
+  virtual bool IsControlFlow() const { return true; }
 
   HBasicBlock* GetSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(Goto);
 
  private:
@@ -873,10 +942,12 @@
 // two successors.
 class HIf : public HTemplateInstruction<1> {
  public:
-  explicit HIf(HInstruction* input) {
+  explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, input);
   }
 
+  virtual bool IsControlFlow() const { return true; }
+
   HBasicBlock* IfTrueSuccessor() const {
     return GetBlock()->GetSuccessors().Get(0);
   }
@@ -885,8 +956,6 @@
     return GetBlock()->GetSuccessors().Get(1);
   }
 
-  virtual bool IsControlFlow() const { return true; }
-
   DECLARE_INSTRUCTION(If);
 
   virtual bool IsIfInstruction() const { return true; }
@@ -899,7 +968,7 @@
  public:
   HBinaryOperation(Primitive::Type result_type,
                    HInstruction* left,
-                   HInstruction* right) : HExpression(result_type) {
+                   HInstruction* right) : HExpression(result_type, SideEffects::None()) {
     SetRawInputAt(0, left);
     SetRawInputAt(1, right);
   }
@@ -910,6 +979,9 @@
 
   virtual bool IsCommutative() { return false; }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
 };
@@ -1041,7 +1113,8 @@
 // A local in the graph. Corresponds to a Dex register.
 class HLocal : public HTemplateInstruction<0> {
  public:
-  explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) {}
+  explicit HLocal(uint16_t reg_number)
+      : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
 
   DECLARE_INSTRUCTION(Local);
 
@@ -1057,7 +1130,8 @@
 // Load a given local. The local is an input of this instruction.
 class HLoadLocal : public HExpression<1> {
  public:
-  explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) {
+  explicit HLoadLocal(HLocal* local, Primitive::Type type)
+      : HExpression(type, SideEffects::None()) {
     SetRawInputAt(0, local);
   }
 
@@ -1073,7 +1147,7 @@
 // and the local.
 class HStoreLocal : public HTemplateInstruction<2> {
  public:
-  HStoreLocal(HLocal* local, HInstruction* value) {
+  HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
     SetRawInputAt(0, local);
     SetRawInputAt(1, value);
   }
@@ -1088,7 +1162,9 @@
 
 class HConstant : public HExpression<0> {
  public:
-  explicit HConstant(Primitive::Type type) : HExpression(type) {}
+  explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+
+  virtual bool CanBeMoved() const { return true; }
 
   DECLARE_INSTRUCTION(Constant);
 
@@ -1104,6 +1180,10 @@
 
   int32_t GetValue() const { return value_; }
 
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return other->AsIntConstant()->value_ == value_;
+  }
+
   DECLARE_INSTRUCTION(IntConstant);
 
  private:
@@ -1118,6 +1198,10 @@
 
   int64_t GetValue() const { return value_; }
 
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    return other->AsLongConstant()->value_ == value_;
+  }
+
   DECLARE_INSTRUCTION(LongConstant);
 
  private:
@@ -1132,7 +1216,8 @@
           uint32_t number_of_arguments,
           Primitive::Type return_type,
           uint32_t dex_pc)
-    : inputs_(arena, number_of_arguments),
+    : HInstruction(SideEffects::All()),
+      inputs_(arena, number_of_arguments),
       return_type_(return_type),
       dex_pc_(dex_pc) {
     inputs_.SetSize(number_of_arguments);
@@ -1188,8 +1273,10 @@
 
 class HNewInstance : public HExpression<0> {
  public:
-  HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot),
-    dex_pc_(dex_pc), type_index_(type_index) {}
+  HNewInstance(uint32_t dex_pc, uint16_t type_index)
+      : HExpression(Primitive::kPrimNot, SideEffects::None()),
+        dex_pc_(dex_pc),
+        type_index_(type_index) {}
 
   uint32_t GetDexPc() const { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
@@ -1237,7 +1324,7 @@
 class HParameterValue : public HExpression<0> {
  public:
   HParameterValue(uint8_t index, Primitive::Type parameter_type)
-      : HExpression(parameter_type), index_(index) {}
+      : HExpression(parameter_type, SideEffects::None()), index_(index) {}
 
   uint8_t GetIndex() const { return index_; }
 
@@ -1253,10 +1340,13 @@
 
 class HNot : public HExpression<1> {
  public:
-  explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) {
+  explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) {
     SetRawInputAt(0, input);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(Not);
 
  private:
@@ -1266,7 +1356,8 @@
 class HPhi : public HInstruction {
  public:
   HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
-      : inputs_(arena, number_of_inputs),
+      : HInstruction(SideEffects::None()),
+        inputs_(arena, number_of_inputs),
         reg_number_(reg_number),
         type_(type),
         is_live_(false) {
@@ -1306,10 +1397,13 @@
 class HNullCheck : public HExpression<1> {
  public:
   HNullCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType()), dex_pc_(dex_pc) {
+      : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
     SetRawInputAt(0, value);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   virtual bool NeedsEnvironment() const { return true; }
 
   uint32_t GetDexPc() const { return dex_pc_; }
@@ -1324,13 +1418,15 @@
 
 class FieldInfo : public ValueObject {
  public:
-  explicit FieldInfo(MemberOffset field_offset)
-      : field_offset_(field_offset) {}
+  explicit FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
+      : field_offset_(field_offset), field_type_(field_type) {}
 
   MemberOffset GetFieldOffset() const { return field_offset_; }
+  Primitive::Type GetFieldType() const { return field_type_; }
 
  private:
   const MemberOffset field_offset_;
+  const Primitive::Type field_type_;
 };
 
 class HInstanceFieldGet : public HExpression<1> {
@@ -1338,11 +1434,19 @@
   HInstanceFieldGet(HInstruction* value,
                     Primitive::Type field_type,
                     MemberOffset field_offset)
-      : HExpression(field_type), field_info_(field_offset) {
+      : HExpression(field_type, SideEffects::DependsOnSomething()),
+        field_info_(field_offset, field_type) {
     SetRawInputAt(0, value);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const {
+    size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
+    return other_offset == GetFieldOffset().SizeValue();
+  }
+
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
 
   DECLARE_INSTRUCTION(InstanceFieldGet);
 
@@ -1356,13 +1460,16 @@
  public:
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
+                    Primitive::Type field_type,
                     MemberOffset field_offset)
-      : field_info_(field_offset) {
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        field_info_(field_offset, field_type) {
     SetRawInputAt(0, object);
     SetRawInputAt(1, value);
   }
 
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+  Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
 
   DECLARE_INSTRUCTION(InstanceFieldSet);
 
@@ -1375,11 +1482,14 @@
 class HArrayGet : public HExpression<2> {
  public:
   HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
-      : HExpression(type) {
+      : HExpression(type, SideEffects::DependsOnSomething()) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(ArrayGet);
 
  private:
@@ -1391,7 +1501,11 @@
   HArraySet(HInstruction* array,
             HInstruction* index,
             HInstruction* value,
-            uint32_t dex_pc) : dex_pc_(dex_pc) {
+            Primitive::Type component_type,
+            uint32_t dex_pc)
+      : HTemplateInstruction(SideEffects::ChangesSomething()),
+        dex_pc_(dex_pc),
+        component_type_(component_type) {
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
     SetRawInputAt(2, value);
@@ -1405,20 +1519,29 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
+  Primitive::Type GetComponentType() const { return component_type_; }
+
   DECLARE_INSTRUCTION(ArraySet);
 
  private:
   const uint32_t dex_pc_;
+  const Primitive::Type component_type_;
 
   DISALLOW_COPY_AND_ASSIGN(HArraySet);
 };
 
 class HArrayLength : public HExpression<1> {
  public:
-  explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+  explicit HArrayLength(HInstruction* array)
+      : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+    // Note that arrays do not change length, so the instruction does not
+    // depend on any write.
     SetRawInputAt(0, array);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   DECLARE_INSTRUCTION(ArrayLength);
 
  private:
@@ -1428,12 +1551,15 @@
 class HBoundsCheck : public HExpression<2> {
  public:
   HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
-      : HExpression(index->GetType()), dex_pc_(dex_pc) {
+      : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
     DCHECK(index->GetType() == Primitive::kPrimInt);
     SetRawInputAt(0, index);
     SetRawInputAt(1, length);
   }
 
+  virtual bool CanBeMoved() const { return true; }
+  virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
   virtual bool NeedsEnvironment() const { return true; }
 
   uint32_t GetDexPc() const { return dex_pc_; }
@@ -1455,7 +1581,7 @@
  */
 class HTemporary : public HTemplateInstruction<0> {
  public:
-  explicit HTemporary(size_t index) : index_(index) {}
+  explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
 
   size_t GetIndex() const { return index_; }
 
@@ -1529,7 +1655,8 @@
 
 class HParallelMove : public HTemplateInstruction<0> {
  public:
-  explicit HParallelMove(ArenaAllocator* arena) : moves_(arena, kDefaultNumberOfMoves) {}
+  explicit HParallelMove(ArenaAllocator* arena)
+      : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
 
   void AddMove(MoveOperands* move) {
     moves_.Add(move);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3461276..75f4155 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -240,8 +240,27 @@
 
     visualizer.DumpGraph(kRegisterAllocatorPassName);
     codegen->CompileOptimized(&allocator);
+
+    std::vector<uint8_t> mapping_table;
+    SrcMap src_mapping_table;
+    codegen->BuildMappingTable(&mapping_table,
+            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+                 &src_mapping_table : nullptr);
+
+    std::vector<uint8_t> stack_map;
+    codegen->BuildStackMaps(&stack_map);
+
+    return new CompiledMethod(GetCompilerDriver(),
+                              instruction_set,
+                              allocator.GetMemory(),
+                              codegen->GetFrameSize(),
+                              codegen->GetCoreSpillMask(),
+                              0, /* FPR spill mask, unused */
+                              mapping_table,
+                              stack_map);
   } else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
     LOG(FATAL) << "Could not allocate registers in optimizing compiler";
+    return nullptr;
   } else {
     codegen->CompileBaseline(&allocator);
 
@@ -253,29 +272,29 @@
     SsaLivenessAnalysis liveness(*graph, codegen);
     liveness.Analyze();
     visualizer.DumpGraph(kLivenessPassName);
+
+    std::vector<uint8_t> mapping_table;
+    SrcMap src_mapping_table;
+    codegen->BuildMappingTable(&mapping_table,
+            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+                 &src_mapping_table : nullptr);
+    std::vector<uint8_t> vmap_table;
+    codegen->BuildVMapTable(&vmap_table);
+    std::vector<uint8_t> gc_map;
+    codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+    return new CompiledMethod(GetCompilerDriver(),
+                              instruction_set,
+                              allocator.GetMemory(),
+                              codegen->GetFrameSize(),
+                              codegen->GetCoreSpillMask(),
+                              0, /* FPR spill mask, unused */
+                              &src_mapping_table,
+                              mapping_table,
+                              vmap_table,
+                              gc_map,
+                              nullptr);
   }
-
-  std::vector<uint8_t> mapping_table;
-  SrcMap src_mapping_table;
-  codegen->BuildMappingTable(&mapping_table,
-          GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
-               &src_mapping_table : nullptr);
-  std::vector<uint8_t> vmap_table;
-  codegen->BuildVMapTable(&vmap_table);
-  std::vector<uint8_t> gc_map;
-  codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
-
-  return new CompiledMethod(GetCompilerDriver(),
-                            instruction_set,
-                            allocator.GetMemory(),
-                            codegen->GetFrameSize(),
-                            codegen->GetCoreSpillMask(),
-                            0, /* FPR spill mask, unused */
-                            &src_mapping_table,
-                            mapping_table,
-                            vmap_table,
-                            gc_map,
-                            nullptr);
 }
 
 CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index da13b1e..54888ba 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -31,18 +31,26 @@
       : allocator_(allocator),
         codegen_(codegen),
         liveness_(liveness),
-        unhandled_(allocator, 0),
+        unhandled_core_intervals_(allocator, 0),
+        unhandled_fp_intervals_(allocator, 0),
+        unhandled_(nullptr),
         handled_(allocator, 0),
         active_(allocator, 0),
         inactive_(allocator, 0),
         physical_register_intervals_(allocator, codegen->GetNumberOfRegisters()),
+        temp_intervals_(allocator, 4),
         spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+        safepoints_(allocator, 0),
         processing_core_registers_(false),
         number_of_registers_(-1),
         registers_array_(nullptr),
-        blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())) {
+        blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())),
+        reserved_out_slots_(0) {
   codegen->SetupBlockedRegisters(blocked_registers_);
   physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
+  // Always reserve for the current method and the graph's max out registers.
+  // TODO: compute it instead.
+  reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs();
 }
 
 bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph,
@@ -55,7 +63,6 @@
          !it.Done();
          it.Advance()) {
       HInstruction* current = it.Current();
-      if (current->NeedsEnvironment()) return false;
       if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
       if (current->GetType() == Primitive::kPrimFloat) return false;
       if (current->GetType() == Primitive::kPrimDouble) return false;
@@ -65,17 +72,14 @@
 }
 
 static bool ShouldProcess(bool processing_core_registers, LiveInterval* interval) {
+  if (interval == nullptr) return false;
   bool is_core_register = (interval->GetType() != Primitive::kPrimDouble)
       && (interval->GetType() != Primitive::kPrimFloat);
   return processing_core_registers == is_core_register;
 }
 
 void RegisterAllocator::AllocateRegisters() {
-  processing_core_registers_ = true;
   AllocateRegistersInternal();
-  processing_core_registers_ = false;
-  AllocateRegistersInternal();
-
   Resolve();
 
   if (kIsDebugBuild) {
@@ -101,78 +105,120 @@
   interval->AddRange(start, end);
 }
 
-// TODO: make the register allocator understand instructions like HCondition
-// that may not need to be materialized.  It doesn't need to allocate any
-// registers for it.
 void RegisterAllocator::AllocateRegistersInternal() {
-  number_of_registers_ = processing_core_registers_
-      ? codegen_->GetNumberOfCoreRegisters()
-      : codegen_->GetNumberOfFloatingPointRegisters();
-
-  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
-
   // Iterate post-order, to ensure the list is sorted, and the last added interval
   // is the one with the lowest start position.
-  for (size_t i = liveness_.GetNumberOfSsaValues(); i > 0; --i) {
-    HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i - 1);
-    LiveInterval* current = instruction->GetLiveInterval();
-    if (ShouldProcess(processing_core_registers_, current)) {
-      DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-
-      LocationSummary* locations = instruction->GetLocations();
-      if (locations->GetTempCount() != 0) {
-        // Note that we already filtered out instructions requiring temporaries in
-        // RegisterAllocator::CanAllocateRegistersFor.
-        LOG(FATAL) << "Unimplemented";
-      }
-
-      // Some instructions define their output in fixed register/stack slot. We need
-      // to ensure we know these locations before doing register allocation. For a
-      // given register, we create an interval that covers these locations. The register
-      // will be unavailable at these locations when trying to allocate one for an
-      // interval.
-      //
-      // The backwards walking ensures the ranges are ordered on increasing start positions.
-      Location output = locations->Out();
-      size_t position = instruction->GetLifetimePosition();
-      if (output.IsRegister()) {
-        // Shift the interval's start by one to account for the blocked register.
-        current->SetFrom(position + 1);
-        current->SetRegister(output.reg().RegId());
-        BlockRegister(output, position, position + 1, instruction->GetType());
-      } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
-        current->SetSpillSlot(output.GetStackIndex());
-      }
-      for (size_t i = 0; i < instruction->InputCount(); ++i) {
-        Location input = locations->InAt(i);
-        if (input.IsRegister()) {
-          BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
-        }
-      }
-
-      // Add the interval to the correct list.
-      if (current->HasRegister()) {
-        DCHECK(instruction->IsParameterValue());
-        inactive_.Add(current);
-      } else if (current->HasSpillSlot() || instruction->IsConstant()) {
-        // Split before first register use.
-        size_t first_register_use = current->FirstRegisterUse();
-        if (first_register_use != kNoLifetime) {
-          LiveInterval* split = Split(current, first_register_use - 1);
-          // Don't add direclty to `unhandled_`, it needs to be sorted and the start
-          // of this new interval might be after intervals already in the list.
-          AddToUnhandled(split);
-        } else {
-          // Nothing to do, we won't allocate a register for this value.
-        }
-      } else {
-        DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-        unhandled_.Add(current);
-      }
+  for (HLinearPostOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+      ProcessInstruction(it.Current());
+    }
+    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+      ProcessInstruction(it.Current());
     }
   }
 
+  number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
+  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+  processing_core_registers_ = true;
+  unhandled_ = &unhandled_core_intervals_;
   LinearScan();
+
+  inactive_.Reset();
+  active_.Reset();
+  handled_.Reset();
+
+  number_of_registers_ = codegen_->GetNumberOfFloatingPointRegisters();
+  registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+  processing_core_registers_ = false;
+  unhandled_ = &unhandled_fp_intervals_;
+  // TODO: Enable FP register allocation.
+  DCHECK(unhandled_->IsEmpty());
+  LinearScan();
+}
+
+void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  size_t position = instruction->GetLifetimePosition();
+
+  if (locations == nullptr) return;
+
+  // Create synthesized intervals for temporaries.
+  for (size_t i = 0; i < locations->GetTempCount(); ++i) {
+    Location temp = locations->GetTemp(i);
+    if (temp.IsRegister()) {
+      BlockRegister(temp, position, position + 1, Primitive::kPrimInt);
+    } else {
+      LiveInterval* interval =
+          LiveInterval::MakeTempInterval(allocator_, instruction, Primitive::kPrimInt);
+      temp_intervals_.Add(interval);
+      interval->AddRange(position, position + 1);
+      unhandled_core_intervals_.Add(interval);
+    }
+  }
+
+  if (locations->CanCall()) {
+    codegen_->MarkNotLeaf();
+    safepoints_.Add(instruction);
+    // Block all registers.
+    for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+      BlockRegister(Location::RegisterLocation(ManagedRegister(i)),
+                    position,
+                    position + 1,
+                    Primitive::kPrimInt);
+    }
+  }
+
+  for (size_t i = 0; i < instruction->InputCount(); ++i) {
+    Location input = locations->InAt(i);
+    if (input.IsRegister()) {
+      BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
+    }
+  }
+
+  bool core_register = (instruction->GetType() != Primitive::kPrimDouble)
+      && (instruction->GetType() != Primitive::kPrimFloat);
+  GrowableArray<LiveInterval*>& unhandled = core_register
+      ? unhandled_core_intervals_
+      : unhandled_fp_intervals_;
+
+  LiveInterval* current = instruction->GetLiveInterval();
+  if (current == nullptr) return;
+
+  DCHECK(unhandled.IsEmpty() || current->StartsBefore(unhandled.Peek()));
+  // Some instructions define their output in fixed register/stack slot. We need
+  // to ensure we know these locations before doing register allocation. For a
+  // given register, we create an interval that covers these locations. The register
+  // will be unavailable at these locations when trying to allocate one for an
+  // interval.
+  //
+  // The backwards walking ensures the ranges are ordered on increasing start positions.
+  Location output = locations->Out();
+  if (output.IsRegister()) {
+    // Shift the interval's start by one to account for the blocked register.
+    current->SetFrom(position + 1);
+    current->SetRegister(output.reg().RegId());
+    BlockRegister(output, position, position + 1, instruction->GetType());
+  } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
+    current->SetSpillSlot(output.GetStackIndex());
+  }
+
+  // If needed, add interval to the list of unhandled intervals.
+  if (current->HasSpillSlot() || instruction->IsConstant()) {
+    // Split before first register use.
+    size_t first_register_use = current->FirstRegisterUse();
+    if (first_register_use != kNoLifetime) {
+      LiveInterval* split = Split(current, first_register_use - 1);
+      // Don't add direclty to `unhandled`, it needs to be sorted and the start
+      // of this new interval might be after intervals already in the list.
+      AddSorted(&unhandled, split);
+    } else {
+      // Nothing to do, we won't allocate a register for this value.
+    }
+  } else {
+    DCHECK(unhandled.IsEmpty() || current->StartsBefore(unhandled.Peek()));
+    unhandled.Add(current);
+  }
 }
 
 class AllRangesIterator : public ValueObject {
@@ -220,12 +266,20 @@
     }
   }
 
-  return ValidateIntervals(intervals, spill_slots_.Size(), *codegen_, allocator_,
-                           processing_core_registers_, log_fatal_on_failure);
+  for (size_t i = 0, e = temp_intervals_.Size(); i < e; ++i) {
+    LiveInterval* temp = temp_intervals_.Get(i);
+    if (ShouldProcess(processing_core_registers_, temp)) {
+      intervals.Add(temp);
+    }
+  }
+
+  return ValidateIntervals(intervals, spill_slots_.Size(), reserved_out_slots_, *codegen_,
+                           allocator_, processing_core_registers_, log_fatal_on_failure);
 }
 
 bool RegisterAllocator::ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
                                           size_t number_of_spill_slots,
+                                          size_t number_of_out_slots,
                                           const CodeGenerator& codegen,
                                           ArenaAllocator* allocator,
                                           bool processing_core_registers,
@@ -249,8 +303,9 @@
       if (current->GetParent()->HasSpillSlot()
            // Parameters have their own stack slot.
            && !(defined_by != nullptr && defined_by->IsParameterValue())) {
-        BitVector* liveness_of_spill_slot = liveness_of_values.Get(
-            number_of_registers + current->GetParent()->GetSpillSlot() / kVRegSize);
+        BitVector* liveness_of_spill_slot = liveness_of_values.Get(number_of_registers
+            + current->GetParent()->GetSpillSlot() / kVRegSize
+            - number_of_out_slots);
         for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
           if (liveness_of_spill_slot->IsBitSet(j)) {
             if (log_fatal_on_failure) {
@@ -272,7 +327,11 @@
           if (liveness_of_register->IsBitSet(j)) {
             if (log_fatal_on_failure) {
               std::ostringstream message;
-              message << "Register conflict at " << j << " for ";
+              message << "Register conflict at " << j << " ";
+              if (defined_by != nullptr) {
+                message << "(" << defined_by->DebugName() << ")";
+              }
+              message << "for ";
               if (processing_core_registers) {
                 codegen.DumpCoreRegister(message, current->GetRegister());
               } else {
@@ -309,10 +368,10 @@
 
 // By the book implementation of a linear scan register allocator.
 void RegisterAllocator::LinearScan() {
-  while (!unhandled_.IsEmpty()) {
+  while (!unhandled_->IsEmpty()) {
     // (1) Remove interval with the lowest start position from unhandled.
-    LiveInterval* current = unhandled_.Pop();
-    DCHECK(!current->IsFixed() && !current->HasRegister() && !current->HasSpillSlot());
+    LiveInterval* current = unhandled_->Pop();
+    DCHECK(!current->IsFixed() && !current->HasSpillSlot());
     size_t position = current->GetStart();
 
     // (2) Remove currently active intervals that are dead at this position.
@@ -392,13 +451,19 @@
     free_until[interval->GetRegister()] = 0;
   }
 
-  // Pick the register that is free the longest.
   int reg = -1;
-  for (size_t i = 0; i < number_of_registers_; ++i) {
-    if (IsBlocked(i)) continue;
-    if (reg == -1 || free_until[i] > free_until[reg]) {
-      reg = i;
-      if (free_until[i] == kMaxLifetimePosition) break;
+  if (current->HasRegister()) {
+    // Some instructions have a fixed register output.
+    reg = current->GetRegister();
+    DCHECK_NE(free_until[reg], 0u);
+  } else {
+    // Pick the register that is free the longest.
+    for (size_t i = 0; i < number_of_registers_; ++i) {
+      if (IsBlocked(i)) continue;
+      if (reg == -1 || free_until[i] > free_until[reg]) {
+        reg = i;
+        if (free_until[i] == kMaxLifetimePosition) break;
+      }
     }
   }
 
@@ -414,7 +479,7 @@
     // the register is not available anymore.
     LiveInterval* split = Split(current, free_until[reg]);
     DCHECK(split != nullptr);
-    AddToUnhandled(split);
+    AddSorted(unhandled_, split);
   }
   return true;
 }
@@ -493,7 +558,7 @@
     // register, we split this interval just before its first register use.
     AllocateSpillSlotFor(current);
     LiveInterval* split = Split(current, first_register_use - 1);
-    AddToUnhandled(split);
+    AddSorted(unhandled_, split);
     return false;
   } else {
     // Use this register and spill the active and inactives interval that
@@ -507,7 +572,7 @@
         LiveInterval* split = Split(active, current->GetStart());
         active_.DeleteAt(i);
         handled_.Add(active);
-        AddToUnhandled(split);
+        AddSorted(unhandled_, split);
         break;
       }
     }
@@ -519,12 +584,12 @@
         if (next_intersection != kNoLifetime) {
           if (inactive->IsFixed()) {
             LiveInterval* split = Split(current, next_intersection);
-            AddToUnhandled(split);
+            AddSorted(unhandled_, split);
           } else {
             LiveInterval* split = Split(inactive, current->GetStart());
             inactive_.DeleteAt(i);
             handled_.Add(inactive);
-            AddToUnhandled(split);
+            AddSorted(unhandled_, split);
             --i;
           }
         }
@@ -535,16 +600,16 @@
   }
 }
 
-void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) {
   size_t insert_at = 0;
-  for (size_t i = unhandled_.Size(); i > 0; --i) {
-    LiveInterval* current = unhandled_.Get(i - 1);
+  for (size_t i = array->Size(); i > 0; --i) {
+    LiveInterval* current = array->Get(i - 1);
     if (current->StartsAfter(interval)) {
       insert_at = i;
       break;
     }
   }
-  unhandled_.InsertAt(insert_at, interval);
+  array->InsertAt(insert_at, interval);
 }
 
 LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
@@ -624,7 +689,7 @@
     spill_slots_.Put(slot + 1, end);
   }
 
-  parent->SetSpillSlot(slot * kVRegSize);
+  parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
 }
 
 void RegisterAllocator::AllocateOneSpillSlot(LiveInterval* parent, size_t end) {
@@ -643,7 +708,7 @@
     spill_slots_.Put(slot, end);
   }
 
-  parent->SetSpillSlot(slot * kVRegSize);
+  parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
 }
 
 static Location ConvertToLocation(LiveInterval* interval) {
@@ -820,8 +885,10 @@
     // Walk over all uses covered by this interval, and update the location
     // information.
     while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
-      if (!use->GetIsEnvironment()) {
-        LocationSummary* locations = use->GetUser()->GetLocations();
+      LocationSummary* locations = use->GetUser()->GetLocations();
+      if (use->GetIsEnvironment()) {
+        locations->SetEnvironmentAt(use->GetInputIndex(), source);
+      } else {
         Location expected_location = locations->InAt(use->GetInputIndex());
         if (expected_location.IsUnallocated()) {
           locations->SetInAt(use->GetInputIndex(), source);
@@ -841,6 +908,38 @@
       Location destination = ConvertToLocation(next_sibling);
       InsertParallelMoveAt(current->GetEnd(), source, destination);
     }
+
+    // At each safepoint, we record stack and register information.
+    for (size_t i = 0, e = safepoints_.Size(); i < e; ++i) {
+      HInstruction* safepoint = safepoints_.Get(i);
+      size_t position = safepoint->GetLifetimePosition();
+      LocationSummary* locations = safepoint->GetLocations();
+      if (!current->Covers(position)) continue;
+
+      if (current->GetType() == Primitive::kPrimNot) {
+        DCHECK(current->GetParent()->HasSpillSlot());
+        locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
+      }
+
+      switch (source.GetKind()) {
+        case Location::kRegister: {
+          locations->SetLiveRegister(source.reg().RegId());
+          if (current->GetType() == Primitive::kPrimNot) {
+            locations->SetRegisterBit(source.reg().RegId());
+          }
+          break;
+        }
+        case Location::kStackSlot:  // Fall-through
+        case Location::kDoubleStackSlot:  // Fall-through
+        case Location::kConstant: {
+          // Nothing to do.
+          break;
+        }
+        default: {
+          LOG(FATAL) << "Unexpected location for object";
+        }
+      }
+    }
     current = next_sibling;
   } while (current != nullptr);
   DCHECK(use == nullptr);
@@ -907,7 +1006,7 @@
 }
 
 void RegisterAllocator::Resolve() {
-  codegen_->ComputeFrameSize(spill_slots_.Size());
+  codegen_->ComputeFrameSize(spill_slots_.Size(), reserved_out_slots_);
 
   // Adjust the Out Location of instructions.
   // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -978,6 +1077,20 @@
       }
     }
   }
+
+  // Assign temp locations.
+  HInstruction* current = nullptr;
+  size_t temp_index = 0;
+  for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
+    LiveInterval* temp = temp_intervals_.Get(i);
+    if (temp->GetDefinedBy() != current) {
+      temp_index = 0;
+      current = temp->GetDefinedBy();
+    }
+    LocationSummary* locations = current->GetLocations();
+    locations->SetTempAt(
+        temp_index++, Location::RegisterLocation(ManagedRegister(temp->GetRegister())));
+  }
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index be1c7ec..f737491 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -59,6 +59,7 @@
   // Helper method for validation. Used by unit testing.
   static bool ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
                                 size_t number_of_spill_slots,
+                                size_t number_of_out_slots,
                                 const CodeGenerator& codegen,
                                 ArenaAllocator* allocator,
                                 bool processing_core_registers,
@@ -83,8 +84,8 @@
   bool AllocateBlockedReg(LiveInterval* interval);
   void Resolve();
 
-  // Add `interval` in the sorted list of unhandled intervals.
-  void AddToUnhandled(LiveInterval* interval);
+  // Add `interval` in the given sorted list.
+  static void AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval);
 
   // Split `interval` at the position `at`. The new interval starts at `at`.
   LiveInterval* Split(LiveInterval* interval, size_t at);
@@ -115,6 +116,7 @@
 
   // Helper methods.
   void AllocateRegistersInternal();
+  void ProcessInstruction(HInstruction* instruction);
   bool ValidateInternal(bool log_fatal_on_failure) const;
   void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
 
@@ -122,9 +124,17 @@
   CodeGenerator* const codegen_;
   const SsaLivenessAnalysis& liveness_;
 
-  // List of intervals that must be processed, ordered by start position. Last entry
-  // is the interval that has the lowest start position.
-  GrowableArray<LiveInterval*> unhandled_;
+  // List of intervals for core registers that must be processed, ordered by start
+  // position. Last entry is the interval that has the lowest start position.
+  // This list is initially populated before doing the linear scan.
+  GrowableArray<LiveInterval*> unhandled_core_intervals_;
+
+  // List of intervals for floating-point registers. Same comments as above.
+  GrowableArray<LiveInterval*> unhandled_fp_intervals_;
+
+  // Currently processed list of unhandled intervals. Either `unhandled_core_intervals_`
+  // or `unhandled_fp_intervals_`.
+  GrowableArray<LiveInterval*>* unhandled_;
 
   // List of intervals that have been processed.
   GrowableArray<LiveInterval*> handled_;
@@ -137,13 +147,20 @@
   // That is, they have a lifetime hole that spans the start of the new interval.
   GrowableArray<LiveInterval*> inactive_;
 
-  // Fixed intervals for physical registers. Such an interval covers the positions
+  // Fixed intervals for physical registers. Such intervals cover the positions
   // where an instruction requires a specific register.
   GrowableArray<LiveInterval*> physical_register_intervals_;
 
+  // Intervals for temporaries. Such intervals cover the positions
+  // where an instruction requires a temporary.
+  GrowableArray<LiveInterval*> temp_intervals_;
+
   // The spill slots allocated for live intervals.
   GrowableArray<size_t> spill_slots_;
 
+  // Instructions that need a safepoint.
+  GrowableArray<HInstruction*> safepoints_;
+
   // True if processing core registers. False if processing floating
   // point registers.
   bool processing_core_registers_;
@@ -157,6 +174,9 @@
   // Blocked registers, as decided by the code generator.
   bool* const blocked_registers_;
 
+  // Slots reserved for out arguments.
+  size_t reserved_out_slots_;
+
   DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
 };
 
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index bafe577..7539d44 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -66,11 +66,11 @@
     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -81,11 +81,11 @@
     static constexpr size_t ranges2[][2] = {{42, 43}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -96,11 +96,11 @@
     static constexpr size_t ranges2[][2] = {{42, 43}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -111,11 +111,11 @@
     static constexpr size_t ranges2[][2] = {{42, 47}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
     intervals.Reset();
   }
 
@@ -127,16 +127,16 @@
     static constexpr size_t ranges2[][2] = {{42, 47}};
     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(1)->SetRegister(0);
     // Sibling of the first interval has no register allocated to it.
     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
 
     intervals.Get(0)->GetNextSibling()->SetRegister(0);
     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
-        intervals, 0, *codegen, &allocator, true, false));
+        intervals, 0, 0, *codegen, &allocator, true, false));
   }
 }
 
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83035b5..33b1f1f 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -47,7 +47,7 @@
 };
 
 /**
- * A live range contains the start and end of a range where an instruction
+ * A live range contains the start and end of a range where an instruction or a temporary
  * is live.
  */
 class LiveRange : public ArenaObject {
@@ -76,7 +76,7 @@
 
  private:
   size_t start_;
-  size_t end_;
+  const size_t end_;
   LiveRange* next_;
 
   friend class LiveInterval;
@@ -133,7 +133,12 @@
  */
 class LiveInterval : public ArenaObject {
  public:
-  LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr)
+  LiveInterval(ArenaAllocator* allocator,
+               Primitive::Type type,
+               HInstruction* defined_by = nullptr,
+               bool is_fixed = false,
+               int reg = kNoRegister,
+               bool is_temp = false)
       : allocator_(allocator),
         first_range_(nullptr),
         last_range_(nullptr),
@@ -141,16 +146,20 @@
         type_(type),
         next_sibling_(nullptr),
         parent_(this),
-        register_(kNoRegister),
+        register_(reg),
         spill_slot_(kNoSpillSlot),
-        is_fixed_(false),
+        is_fixed_(is_fixed),
+        is_temp_(is_temp),
         defined_by_(defined_by) {}
 
   static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) {
-    LiveInterval* interval = new (allocator) LiveInterval(allocator, type);
-    interval->SetRegister(reg);
-    interval->is_fixed_ = true;
-    return interval;
+    return new (allocator) LiveInterval(allocator, type, nullptr, true, reg, false);
+  }
+
+  static LiveInterval* MakeTempInterval(ArenaAllocator* allocator,
+                                        HInstruction* defined_by,
+                                        Primitive::Type type) {
+    return new (allocator) LiveInterval(allocator, type, defined_by, false, kNoRegister, true);
   }
 
   bool IsFixed() const { return is_fixed_; }
@@ -192,8 +201,10 @@
     } else if (first_range_->GetStart() == end) {
       // There is a use in the following block.
       first_range_->start_ = start;
+    } else if (first_range_->GetStart() == start && first_range_->GetEnd() == end) {
+      DCHECK(is_fixed_);
     } else {
-      DCHECK(first_range_->GetStart() > end);
+      DCHECK_GT(first_range_->GetStart(), end);
       // There is a hole in the interval. Create a new range.
       first_range_ = new (allocator_) LiveRange(start, end, first_range_);
     }
@@ -215,7 +226,11 @@
   }
 
   bool HasSpillSlot() const { return spill_slot_ != kNoSpillSlot; }
-  void SetSpillSlot(int slot) { spill_slot_ = slot; }
+  void SetSpillSlot(int slot) {
+    DCHECK(!is_fixed_);
+    DCHECK(!is_temp_);
+    spill_slot_ = slot;
+  }
   int GetSpillSlot() const { return spill_slot_; }
 
   void SetFrom(size_t from) {
@@ -243,6 +258,9 @@
   }
 
   bool Covers(size_t position) const {
+    if (IsDeadAt(position)) {
+      return false;
+    }
     LiveRange* current = first_range_;
     while (current != nullptr) {
       if (position >= current->GetStart() && position < current->GetEnd()) {
@@ -288,6 +306,9 @@
   }
 
   size_t FirstRegisterUseAfter(size_t position) const {
+    if (is_temp_) {
+      return position == GetStart() ? position : kNoLifetime;
+    }
     if (position == GetStart() && defined_by_ != nullptr) {
       LocationSummary* locations = defined_by_->GetLocations();
       Location location = locations->Out();
@@ -342,6 +363,7 @@
    * [position ... end)
    */
   LiveInterval* SplitAt(size_t position) {
+    DCHECK(!is_temp_);
     DCHECK(!is_fixed_);
     DCHECK_GT(position, GetStart());
 
@@ -453,7 +475,10 @@
   int spill_slot_;
 
   // Whether the interval is for a fixed register.
-  bool is_fixed_;
+  const bool is_fixed_;
+
+  // Whether the interval is for a temporary.
+  const bool is_temp_;
 
   // The instruction represented by this interval.
   HInstruction* const defined_by_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index a079954..65675dc 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,6 +26,8 @@
       HPhi* phi = it.Current()->AsPhi();
       if (phi->HasEnvironmentUses()) {
         // TODO: Do we want to keep that phi alive?
+        worklist_.Add(phi);
+        phi->SetLive();
         continue;
       }
       for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
@@ -105,7 +107,7 @@
 
     for (size_t i = 1; i < phi->InputCount(); ++i) {
       HInstruction* input = phi->InputAt(i);
-      // For a loop phi, If the input is the phi, the phi is still candidate for
+      // For a loop phi, if the input is the phi, the phi is still candidate for
       // elimination.
       if (input != candidate && input != phi) {
         candidate = nullptr;
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5e1329e..0ea11ad 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -26,9 +26,9 @@
 namespace art {
 
 /**
- * Collects and builds a CodeInfo for a method.
+ * Collects and builds stack maps for a method. All the stack maps
+ * for a method are placed in a CodeInfo object.
  */
-template<typename T>
 class StackMapStream : public ValueObject {
  public:
   explicit StackMapStream(ArenaAllocator* allocator)
@@ -47,7 +47,7 @@
   // See runtime/stack_map.h to know what these fields contain.
   struct StackMapEntry {
     uint32_t dex_pc;
-    T native_pc;
+    uint32_t native_pc_offset;
     uint32_t register_mask;
     BitVector* sp_mask;
     uint32_t num_dex_registers;
@@ -66,14 +66,14 @@
   };
 
   void AddStackMapEntry(uint32_t dex_pc,
-                        T native_pc,
+                        uint32_t native_pc_offset,
                         uint32_t register_mask,
                         BitVector* sp_mask,
                         uint32_t num_dex_registers,
                         uint8_t inlining_depth) {
     StackMapEntry entry;
     entry.dex_pc = dex_pc;
-    entry.native_pc = native_pc;
+    entry.native_pc_offset = native_pc_offset;
     entry.register_mask = register_mask;
     entry.sp_mask = sp_mask;
     entry.num_dex_registers = num_dex_registers;
@@ -82,7 +82,9 @@
     entry.inline_infos_start_index = inline_infos_.Size();
     stack_maps_.Add(entry);
 
-    stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+    if (sp_mask != nullptr) {
+      stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+    }
     if (inlining_depth > 0) {
       number_of_stack_maps_with_inline_info_++;
     }
@@ -102,14 +104,14 @@
   }
 
   size_t ComputeNeededSize() const {
-    return CodeInfo<T>::kFixedSize
+    return CodeInfo::kFixedSize
         + ComputeStackMapSize()
         + ComputeDexRegisterMapSize()
         + ComputeInlineInfoSize();
   }
 
   size_t ComputeStackMapSize() const {
-    return stack_maps_.Size() * (StackMap<T>::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+    return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
   }
 
   size_t ComputeDexRegisterMapSize() const {
@@ -130,11 +132,12 @@
   }
 
   size_t ComputeDexRegisterMapStart() const {
-    return CodeInfo<T>::kFixedSize + ComputeStackMapSize();
+    return CodeInfo::kFixedSize + ComputeStackMapSize();
   }
 
   void FillIn(MemoryRegion region) {
-    CodeInfo<T> code_info(region);
+    CodeInfo code_info(region);
+    code_info.SetOverallSize(region.size());
 
     size_t stack_mask_size = StackMaskEncodingSize(stack_mask_max_);
     uint8_t* memory_start = region.start();
@@ -153,13 +156,15 @@
     uintptr_t next_dex_register_map_offset = 0;
     uintptr_t next_inline_info_offset = 0;
     for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
-      StackMap<T> stack_map = code_info.GetStackMapAt(i);
+      StackMap stack_map = code_info.GetStackMapAt(i);
       StackMapEntry entry = stack_maps_.Get(i);
 
       stack_map.SetDexPc(entry.dex_pc);
-      stack_map.SetNativePc(entry.native_pc);
+      stack_map.SetNativePcOffset(entry.native_pc_offset);
       stack_map.SetRegisterMask(entry.register_mask);
-      stack_map.SetStackMask(*entry.sp_mask);
+      if (entry.sp_mask != nullptr) {
+        stack_map.SetStackMask(*entry.sp_mask);
+      }
 
       // Set the register map.
       MemoryRegion region = dex_register_maps_region.Subregion(
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index a70259e..5ee6ae0 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -34,7 +34,7 @@
 TEST(StackMapTest, Test1) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  StackMapStream<size_t> stream(&arena);
+  StackMapStream stream(&arena);
 
   ArenaBitVector sp_mask(&arena, 0, false);
   stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, 2, 0);
@@ -46,15 +46,15 @@
   MemoryRegion region(memory, size);
   stream.FillIn(region);
 
-  CodeInfo<size_t> code_info(region);
+  CodeInfo code_info(region);
   ASSERT_EQ(0u, code_info.GetStackMaskSize());
   ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
 
-  StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+  StackMap stack_map = code_info.GetStackMapAt(0);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
   ASSERT_EQ(0u, stack_map.GetDexPc());
-  ASSERT_EQ(64u, stack_map.GetNativePc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
   ASSERT_FALSE(stack_map.HasInlineInfo());
 
@@ -71,7 +71,7 @@
 TEST(StackMapTest, Test2) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  StackMapStream<size_t> stream(&arena);
+  StackMapStream stream(&arena);
 
   ArenaBitVector sp_mask1(&arena, 0, true);
   sp_mask1.SetBit(2);
@@ -93,15 +93,15 @@
   MemoryRegion region(memory, size);
   stream.FillIn(region);
 
-  CodeInfo<size_t> code_info(region);
+  CodeInfo code_info(region);
   ASSERT_EQ(1u, code_info.GetStackMaskSize());
   ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
 
-  StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+  StackMap stack_map = code_info.GetStackMapAt(0);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
   ASSERT_EQ(0u, stack_map.GetDexPc());
-  ASSERT_EQ(64u, stack_map.GetNativePc());
+  ASSERT_EQ(64u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
 
   MemoryRegion stack_mask = stack_map.GetStackMask();
@@ -120,9 +120,9 @@
 
   stack_map = code_info.GetStackMapAt(1);
   ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
-  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(128u)));
+  ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
   ASSERT_EQ(1u, stack_map.GetDexPc());
-  ASSERT_EQ(128u, stack_map.GetNativePc());
+  ASSERT_EQ(128u, stack_map.GetNativePcOffset());
   ASSERT_EQ(0xFFu, stack_map.GetRegisterMask());
 
   stack_mask = stack_map.GetStackMask();
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..25d10bd 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
 	oatdump.cc
 
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libart-compiler,art/disassembler art/compiler,target,debug))
 endif
 
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
+  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libart-compiler,art/disassembler art/compiler,host,debug))
 endif
 
 ########################################################################
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 931ee56..4cdf618 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -20,6 +20,7 @@
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/stringpiece.h"
@@ -29,6 +30,7 @@
 #include "dex_file-inl.h"
 #include "dex_instruction.h"
 #include "disassembler.h"
+#include "elf_builder.h"
 #include "field_helper.h"
 #include "gc_map.h"
 #include "gc/space/image_space.h"
@@ -47,6 +49,7 @@
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "os.h"
+#include "output_stream.h"
 #include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
@@ -105,6 +108,224 @@
   "kClassRoots",
 };
 
+class OatSymbolizer : public CodeOutput {
+ public:
+  explicit OatSymbolizer(const OatFile* oat_file, std::string& output_name) :
+      oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr), output_name_(output_name) {}
+
+  bool Init() {
+    Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
+
+    uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+    uint32_t oat_exec_size = diff - oat_data_size;
+
+    if (output_name_.empty()) {
+      output_name_ = "symbolized.oat";
+    }
+    elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
+
+    builder_.reset(new ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                                  Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(
+        this,
+        elf_output_,
+        oat_file_->GetOatHeader().GetInstructionSet(),
+        0,
+        oat_data_size,
+        oat_data_size,
+        oat_exec_size,
+        true,
+        false));
+
+    if (!builder_->Init()) {
+      builder_.reset(nullptr);
+      return false;
+    }
+
+    return true;
+  }
+
+  typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
+                                          uint32_t,
+                                          const OatFile::OatMethod&,
+                                          const DexFile&,
+                                          uint32_t,
+                                          const DexFile::CodeItem*,
+                                          uint32_t);
+
+  bool Symbolize() {
+    if (builder_.get() == nullptr) {
+      return false;
+    }
+
+    Walk(&art::OatSymbolizer::RegisterForDedup);
+
+    NormalizeState();
+
+    Walk(&art::OatSymbolizer::AddSymbol);
+
+    bool result = builder_->Write();
+
+    elf_output_->Flush();
+    elf_output_->Close();
+
+    return result;
+  }
+
+  void Walk(Callback callback) {
+    std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
+    for (size_t i = 0; i < oat_dex_files.size(); i++) {
+      const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+      CHECK(oat_dex_file != NULL);
+      WalkOatDexFile(oat_dex_file, callback);
+    }
+  }
+
+  void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+    std::string error_msg;
+    std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+    if (dex_file.get() == nullptr) {
+      return;
+    }
+    for (size_t class_def_index = 0;
+        class_def_index < dex_file->NumClassDefs();
+        class_def_index++) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+      const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+      OatClassType type = oat_class.GetType();
+      switch (type) {
+        case kOatClassAllCompiled:
+        case kOatClassSomeCompiled:
+          WalkOatClass(oat_class, *dex_file.get(), class_def, callback);
+          break;
+
+        case kOatClassNoneCompiled:
+        case kOatClassMax:
+          // Ignore.
+          break;
+      }
+    }
+  }
+
+  void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
+                    const DexFile::ClassDef& class_def, Callback callback) {
+    const byte* class_data = dex_file.GetClassData(class_def);
+    if (class_data == nullptr) {  // empty class such as a marker interface?
+      return;
+    }
+    // Note: even if this is an interface or a native class, we still have to walk it, as there
+    //       might be a static initializer.
+    ClassDataItemIterator it(dex_file, class_data);
+    SkipAllFields(&it);
+    uint32_t class_method_idx = 0;
+    while (it.HasNextDirectMethod()) {
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+                    it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+      class_method_idx++;
+      it.Next();
+    }
+    while (it.HasNextVirtualMethod()) {
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+                    it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+      class_method_idx++;
+      it.Next();
+    }
+    DCHECK(!it.HasNext());
+  }
+
+  void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                     const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                     uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                     uint32_t method_access_flags, Callback callback) {
+    if ((method_access_flags & kAccAbstract) != 0) {
+      // Abstract method, no code.
+      return;
+    }
+    if (oat_method.GetCodeOffset() == 0) {
+      // No code.
+      return;
+    }
+
+    (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
+                      method_access_flags);
+  }
+
+  void RegisterForDedup(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                        const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                        uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                        uint32_t method_access_flags) {
+    state_[oat_method.GetCodeOffset()]++;
+  }
+
+  void NormalizeState() {
+    for (auto& x : state_) {
+      if (x.second == 1) {
+        state_[x.first] = 0;
+      }
+    }
+  }
+
+  enum class DedupState {  // private
+    kNotDeduplicated,
+    kDeduplicatedFirst,
+    kDeduplicatedOther
+  };
+  DedupState IsDuplicated(uint32_t offset) {
+    if (state_[offset] == 0) {
+      return DedupState::kNotDeduplicated;
+    }
+    if (state_[offset] == 1) {
+      return DedupState::kDeduplicatedOther;
+    }
+    state_[offset] = 1;
+    return DedupState::kDeduplicatedFirst;
+  }
+
+  void AddSymbol(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+                 const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                 uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                 uint32_t method_access_flags) {
+    DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
+    if (dedup != DedupState::kDeduplicatedOther) {
+      std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
+
+      if (dedup == DedupState::kDeduplicatedFirst) {
+        pretty_name = "[Dedup]" + pretty_name;
+      }
+
+      ElfSymtabBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr,
+      Elf32_Sym, Elf32_Shdr>* symtab = &builder_->symtab_builder_;
+
+      symtab->AddSymbol(pretty_name, &builder_->text_builder_, oat_method.GetCodeOffset() -
+                        oat_file_->GetOatHeader().GetExecutableOffset(), true,
+                        oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
+    }
+  }
+
+  // Write oat code. Required by ElfBuilder/CodeOutput.
+  bool Write(OutputStream* out) {
+    return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
+  }
+
+ private:
+  static void SkipAllFields(ClassDataItemIterator* it) {
+    while (it->HasNextStaticField()) {
+      it->Next();
+    }
+    while (it->HasNextInstanceField()) {
+      it->Next();
+    }
+  }
+
+  const OatFile* oat_file_;
+  std::unique_ptr<ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+                              Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> > builder_;
+  File* elf_output_;
+  std::unordered_map<uint32_t, uint32_t> state_;
+  std::string output_name_;
+};
+
 class OatDumper {
  public:
   explicit OatDumper(const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
@@ -165,7 +386,7 @@
                            GetQuickToInterpreterBridgeOffset);
 #undef DUMP_OAT_HEADER_OFFSET
 
-    os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta();
+    os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta() << "\n\n";
 
     os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
     os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
@@ -395,7 +616,11 @@
       DumpSpillMask(*indent2_os, oat_method.GetFpSpillMask(), true);
       *indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
                                   oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
-      DumpVmap(*indent2_os, oat_method);
+
+      if (oat_method.GetNativeGcMap() != nullptr) {
+        // The native GC map is null for methods compiled with the optimizing compiler.
+        DumpVmap(*indent2_os, oat_method);
+      }
       DumpVregLocations(*indent2_os, oat_method, code_item);
       *indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
                                   oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
@@ -1537,8 +1762,10 @@
   std::string elf_filename_prefix;
   std::ostream* os = &std::cout;
   std::unique_ptr<std::ofstream> out;
+  std::string output_name;
   bool dump_raw_mapping_table = false;
   bool dump_raw_gc_map = false;
+  bool symbolize = false;
 
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
@@ -1571,13 +1798,17 @@
           usage();
         }
     } else if (option.starts_with("--output=")) {
-      const char* filename = option.substr(strlen("--output=")).data();
+      output_name = option.substr(strlen("--output=")).ToString();
+      const char* filename = output_name.c_str();
       out.reset(new std::ofstream(filename));
       if (!out->good()) {
         fprintf(stderr, "Failed to open output filename %s\n", filename);
         usage();
       }
       os = out.get();
+    } else if (option.starts_with("--symbolize=")) {
+      oat_filename = option.substr(strlen("--symbolize=")).data();
+      symbolize = true;
     } else {
       fprintf(stderr, "Unknown argument %s\n", option.data());
       usage();
@@ -1602,8 +1833,20 @@
       fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
       return EXIT_FAILURE;
     }
-    OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
-    oat_dumper.Dump(*os);
+    if (symbolize) {
+      OatSymbolizer oat_symbolizer(oat_file, output_name);
+      if (!oat_symbolizer.Init()) {
+        fprintf(stderr, "Failed to initialize symbolizer\n");
+        return EXIT_FAILURE;
+      }
+      if (!oat_symbolizer.Symbolize()) {
+        fprintf(stderr, "Failed to symbolize\n");
+        return EXIT_FAILURE;
+      }
+    } else {
+      OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
+      oat_dumper.Dump(*os);
+    }
     return EXIT_SUCCESS;
   }
 
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 337e5fe..4155b7e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1255,6 +1255,7 @@
     PUSH ecx                      // Pass receiver.
     PUSH eax                      // Pass Method*.
     call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+    SETUP_GOT_NOSAVE
     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.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 23caefc..f01ea0c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -30,22 +30,24 @@
 namespace art {
 
 Mutex* Locks::abort_lock_ = nullptr;
+Mutex* Locks::alloc_tracker_lock_ = nullptr;
 Mutex* Locks::allocated_monitor_ids_lock_ = nullptr;
 Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
 ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr;
 ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
+Mutex* Locks::deoptimization_lock_ = nullptr;
 ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
 Mutex* Locks::jni_libraries_lock_ = nullptr;
 Mutex* Locks::logging_lock_ = nullptr;
 Mutex* Locks::mem_maps_lock_ = nullptr;
 Mutex* Locks::modify_ldt_lock_ = nullptr;
 ReaderWriterMutex* Locks::mutator_lock_ = nullptr;
+Mutex* Locks::profiler_lock_ = nullptr;
 Mutex* Locks::runtime_shutdown_lock_ = nullptr;
 Mutex* Locks::thread_list_lock_ = nullptr;
 Mutex* Locks::thread_list_suspend_thread_lock_ = nullptr;
 Mutex* Locks::thread_suspend_count_lock_ = nullptr;
 Mutex* Locks::trace_lock_ = nullptr;
-Mutex* Locks::profiler_lock_ = nullptr;
 Mutex* Locks::unexpected_signal_lock_ = nullptr;
 Mutex* Locks::intern_table_lock_ = nullptr;
 
@@ -830,21 +832,23 @@
       DCHECK(modify_ldt_lock_ == nullptr);
     }
     DCHECK(abort_lock_ != nullptr);
+    DCHECK(alloc_tracker_lock_ != nullptr);
     DCHECK(allocated_monitor_ids_lock_ != nullptr);
     DCHECK(allocated_thread_ids_lock_ != nullptr);
     DCHECK(breakpoint_lock_ != nullptr);
     DCHECK(classlinker_classes_lock_ != nullptr);
+    DCHECK(deoptimization_lock_ != nullptr);
     DCHECK(heap_bitmap_lock_ != nullptr);
+    DCHECK(intern_table_lock_ != nullptr);
     DCHECK(jni_libraries_lock_ != nullptr);
     DCHECK(logging_lock_ != nullptr);
     DCHECK(mutator_lock_ != nullptr);
+    DCHECK(profiler_lock_ != nullptr);
     DCHECK(thread_list_lock_ != nullptr);
     DCHECK(thread_list_suspend_thread_lock_ != nullptr);
     DCHECK(thread_suspend_count_lock_ != nullptr);
     DCHECK(trace_lock_ != nullptr);
-    DCHECK(profiler_lock_ != nullptr);
     DCHECK(unexpected_signal_lock_ != nullptr);
-    DCHECK(intern_table_lock_ != nullptr);
   } else {
     // Create global locks in level order from highest lock level to lowest.
     LockLevel current_lock_level = kThreadListSuspendThreadLock;
@@ -853,7 +857,12 @@
         new Mutex("thread list suspend thread by .. lock", current_lock_level);
 
     #define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
-      DCHECK_LT(new_level, current_lock_level); \
+      if (new_level >= current_lock_level) { \
+        /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \
+        fprintf(stderr, "New local level %d is not less than current level %d\n", \
+                new_level, current_lock_level); \
+        exit(1); \
+      } \
       current_lock_level = new_level;
 
     UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
@@ -876,6 +885,14 @@
     DCHECK(trace_lock_ == nullptr);
     trace_lock_ = new Mutex("trace lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock);
+    DCHECK(deoptimization_lock_ == nullptr);
+    deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level);
+
+    UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock);
+    DCHECK(alloc_tracker_lock_ == nullptr);
+    alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
     DCHECK(thread_list_lock_ == nullptr);
     thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
@@ -911,7 +928,6 @@
     DCHECK(intern_table_lock_ == nullptr);
     intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
 
-
     UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
     DCHECK(abort_lock_ == nullptr);
     abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2a623fd..6642b1e 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -85,6 +85,7 @@
   kJniLoadLibraryLock,
   kThreadListLock,
   kBreakpointInvokeLock,
+  kAllocTrackerLock,
   kDeoptimizationLock,
   kTraceLock,
   kProfilerLock,
@@ -557,9 +558,17 @@
   // Guards trace (ie traceview) requests.
   static Mutex* trace_lock_ ACQUIRED_AFTER(profiler_lock_);
 
+  // Guards debugger recent allocation records.
+  static Mutex* alloc_tracker_lock_ ACQUIRED_AFTER(trace_lock_);
+
+  // Guards updates to instrumentation to ensure mutual exclusion of
+  // events like deoptimization requests.
+  // TODO: improve name, perhaps instrumentation_update_lock_.
+  static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
+
   // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
   // attaching and detaching.
-  static Mutex* thread_list_lock_ ACQUIRED_AFTER(trace_lock_);
+  static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_);
 
   // Guards maintaining loading library data structures.
   static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
@@ -586,7 +595,7 @@
   static Mutex* intern_table_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
 
   // Have an exclusive aborting thread.
-  static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+  static Mutex* abort_lock_ ACQUIRED_AFTER(intern_table_lock_);
 
   // Allow mutual exclusion when manipulating Thread::suspend_count_.
   // TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ad436d0..9c2cc6c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -785,7 +785,19 @@
   return NULL;
 }
 
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, const char* dex_location,
+
+// Loads all multi dex files from the given oat file returning true on success.
+//
+// Parameters:
+//   oat_file - the oat file to load from
+//   dex_location - the dex location used to generate the oat file
+//   dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
+//   generated - whether or not the oat_file existed before or was just (re)generated
+//   error_msgs - any error messages will be appended here
+//   dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
+static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
+                                         const char* dex_location,
+                                         const uint32_t* dex_location_checksum,
                                          bool generated,
                                          std::vector<std::string>* error_msgs,
                                          std::vector<const DexFile*>* dex_files) {
@@ -800,12 +812,20 @@
     std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
     const char* next_name = next_name_str.c_str();
 
-    uint32_t dex_location_checksum;
-    uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
+    uint32_t next_location_checksum;
+    uint32_t* next_location_checksum_pointer = &next_location_checksum;
     std::string error_msg;
-    if (!DexFile::GetChecksum(next_name, dex_location_checksum_pointer, &error_msg)) {
+    if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
+      // When i=0 the multidex name should be the same as the location name. We already have the
+      // checksum it so we don't need to recompute it.
+      if (dex_location_checksum == nullptr) {
+        next_location_checksum_pointer = nullptr;
+      } else {
+        next_location_checksum = *dex_location_checksum;
+      }
+    } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
       DCHECK_EQ(false, i == 0 && generated);
-      dex_location_checksum_pointer = nullptr;
+      next_location_checksum_pointer = nullptr;
     }
 
     const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
@@ -814,7 +834,7 @@
       if (i == 0 && generated) {
         std::string error_msg;
         error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
-                                 " file'%s'", dex_location, dex_location_checksum,
+                                 " file'%s'", dex_location, next_location_checksum,
                                  oat_file->GetLocation().c_str());
         error_msgs->push_back(error_msg);
       }
@@ -823,8 +843,8 @@
 
     // Checksum test. Test must succeed when generated.
     success = !generated;
-    if (dex_location_checksum_pointer != nullptr) {
-      success = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
+    if (next_location_checksum_pointer != nullptr) {
+      success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
     }
 
     if (success) {
@@ -840,7 +860,7 @@
     // When we generated the file, we expect success, or something is terribly wrong.
     CHECK_EQ(false, generated && !success)
         << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
-        << std::hex << " dex_location_checksum=" << dex_location_checksum
+        << std::hex << " dex_location_checksum=" << next_location_checksum
         << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
   }
 
@@ -877,6 +897,7 @@
   bool have_checksum = true;
   std::string checksum_error_msg;
   if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
+    // This happens for pre-opted files since the corresponding dex files are no longer on disk.
     dex_location_checksum_pointer = nullptr;
     have_checksum = false;
   }
@@ -942,8 +963,9 @@
 
   // 3) If we have an oat file, check all contained multidex files for our dex_location.
   // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
-  bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, false, error_msgs,
-                                              dex_files);
+  bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
+                                              dex_location_checksum_pointer,
+                                              false, error_msgs, dex_files);
   if (success) {
     const OatFile* oat_file = open_oat_file.release();  // Avoid deleting it.
     if (needs_registering) {
@@ -1004,8 +1026,8 @@
   }
 
   // Try to load again, but stronger checks.
-  success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, true, error_msgs,
-                                         dex_files);
+  success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, dex_location_checksum_pointer,
+                                         true, error_msgs, dex_files);
   if (success) {
     RegisterOatFile(open_oat_file.release());
     return true;
@@ -1189,24 +1211,18 @@
     return false;
   }
 
-  if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
-    *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
-                              oat_file->GetLocation().c_str(),
-                              oat_dex_file->GetDexFileLocationChecksum(),
-                              dex_location, dex_location_checksum);
-    return false;
-  }
+  DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
   return true;
 }
 
 bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
                                        const char* dex_location,
+                                       const uint32_t* dex_location_checksum,
                                        std::string* error_msg) {
   CHECK(oat_file != nullptr);
   CHECK(dex_location != nullptr);
   std::unique_ptr<const DexFile> dex_file;
-  uint32_t dex_location_checksum;
-  if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
+  if (dex_location_checksum == nullptr) {
     // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
     // up-to-date. This is the common case in user builds for jar's and apk's in the /system
     // directory.
@@ -1219,13 +1235,13 @@
     }
     dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
   } else {
-    bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, dex_location_checksum,
+    bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
                                                  kRuntimeISA, error_msg);
     if (!verified) {
       return false;
     }
     dex_file.reset(oat_file->GetOatDexFile(dex_location,
-                                           &dex_location_checksum)->OpenDexFile(error_msg));
+                                           dex_location_checksum)->OpenDexFile(error_msg));
   }
   return dex_file.get() != nullptr;
 }
@@ -1249,7 +1265,8 @@
                                        dex_location));
     return nullptr;
   } else if (oat_file->IsExecutable() &&
-             !VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+             !VerifyOatWithDexFile(oat_file.get(), dex_location,
+                                   dex_location_checksum, &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()));
@@ -2076,7 +2093,6 @@
     // The boot class loader, search the boot class path.
     ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
     if (pair.second != nullptr) {
-      StackHandleScope<1> hs(self);
       return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
     } else {
       // The boot class loader is searched ahead of the application class loader, failures are
@@ -2115,6 +2131,87 @@
     }
   } else {
     ScopedObjectAccessUnchecked soa(self);
+    if (class_loader->GetClass() ==
+            soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) &&
+        class_loader->GetParent()->GetClass() ==
+            soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
+      ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+      // Check if this would be found in the parent boot class loader.
+      if (pair.second != nullptr) {
+        mirror::Class* klass = LookupClass(descriptor, nullptr);
+        if (klass != nullptr) {
+          return klass;
+        }
+        klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+                            *pair.second);
+        if (klass == nullptr) {
+          CHECK(self->IsExceptionPending()) << descriptor;
+          self->ClearException();
+        } else {
+          return klass;
+        }
+      } else {
+        // RegisterDexFile may allocate dex caches (and cause thread suspension).
+        StackHandleScope<3> hs(self);
+        // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+        // We need to get the DexPathList and loop through it.
+        Handle<mirror::ArtField> cookie_field =
+            hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
+        Handle<mirror::ArtField> dex_file_field =
+            hs.NewHandle(
+                soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+        mirror::Object* dex_path_list =
+            soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+            GetObject(class_loader.Get());
+        if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
+            cookie_field.Get() != nullptr) {
+          // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+          mirror::Object* dex_elements_obj =
+              soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+              GetObject(dex_path_list);
+          // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+          // at the mCookie which is a DexFile vector.
+          if (dex_elements_obj != nullptr) {
+            Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+                hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+            for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+              mirror::Object* element = dex_elements->GetWithoutChecks(i);
+              if (element == nullptr) {
+                // Should never happen, fall back to java code to throw a NPE.
+                break;
+              }
+              mirror::Object* dex_file = dex_file_field->GetObject(element);
+              if (dex_file != nullptr) {
+                const uint64_t cookie = cookie_field->GetLong(dex_file);
+                auto* dex_files =
+                    reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
+                if (dex_files == nullptr) {
+                  // This should never happen so log a warning.
+                  LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
+                  break;
+                }
+                for (const DexFile* dex_file : *dex_files) {
+                  const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+                  if (dex_class_def != nullptr) {
+                    RegisterDexFile(*dex_file);
+                    mirror::Class* klass =
+                        DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+                    if (klass == nullptr) {
+                      CHECK(self->IsExceptionPending()) << descriptor;
+                      self->ClearException();
+                      // Exit the loop to make the java code generate an exception.
+                      i = dex_elements->GetLength();
+                      break;
+                    }
+                    return klass;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
     ScopedLocalRef<jobject> class_loader_object(soa.Env(),
                                                 soa.AddLocalReference<jobject>(class_loader.Get()));
     std::string class_name_string(DescriptorToDot(descriptor));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7750c8e..d1f5aa0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -613,9 +613,18 @@
                                                              bool* obsolete_file_cleanup_failed)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
-  // verify an oat file with the given dex file. Will return false when the dex file could not be
-  // verified. Will return true otherwise.
+  // Verifies:
+  //  - that the oat file contains the dex file (with a matching checksum, which may be null if the
+  // file was pre-opted)
+  //  - the checksums of the oat file (against the image space)
+  //  - the checksum of the dex file against dex_location_checksum
+  //  - that the dex file can be opened
+  // Returns true iff all verification succeed.
+  //
+  // The dex_location is the dex location as stored in the oat file header.
+  // (see DexFile::GetDexCanonicalLocation for a description of location conventions)
   bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+                            const uint32_t* dex_location_checksum,
                             std::string* error_msg);
 
   mirror::ArtMethod* CreateProxyConstructor(Thread* self, ConstHandle<mirror::Class> klass,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index b3c887e..488e6e7 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -61,7 +61,15 @@
 namespace art {
 
 static const size_t kMaxAllocRecordStackDepth = 16;  // Max 255.
-static const size_t kDefaultNumAllocRecords = 64*1024;  // Must be a power of 2.
+static const size_t kDefaultNumAllocRecords = 64*1024;  // Must be a power of 2. 2BE can hold 64k-1.
+
+// Limit alloc_record_count to the 2BE value that is the limit of the current protocol.
+static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
+  if (alloc_record_count > 0xffff) {
+    return 0xffff;
+  }
+  return alloc_record_count;
+}
 
 class AllocRecordStackTraceElement {
  public:
@@ -116,9 +124,10 @@
 }
 
 void Dbg::TypeCache::Clear() {
-  ScopedObjectAccess soa(Thread::Current());
+  JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+  Thread* self = Thread::Current();
   for (const auto& p : objects_) {
-    soa.Vm()->DeleteWeakGlobalRef(soa.Self(), p.second);
+    vm->DeleteWeakGlobalRef(self, p.second);
   }
   objects_.clear();
 }
@@ -131,8 +140,9 @@
     return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
   }
 
-  void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    type_ = Dbg::GetTypeCache()->Add(t);
+  void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+                                                       Locks::alloc_tracker_lock_) {
+    type_ = Dbg::type_cache_.Add(t);
   }
 
   size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -304,7 +314,6 @@
 static ObjectRegistry* gRegistry = nullptr;
 
 // Recent allocation tracking.
-Mutex* Dbg::alloc_tracker_lock_ = nullptr;
 AllocRecord* Dbg::recent_allocation_records_ = nullptr;  // TODO: CircularBuffer<AllocRecord>
 size_t Dbg::alloc_record_max_ = 0;
 size_t Dbg::alloc_record_head_ = 0;
@@ -312,7 +321,6 @@
 Dbg::TypeCache Dbg::type_cache_;
 
 // Deoptimization support.
-Mutex* Dbg::deoptimization_lock_ = nullptr;
 std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
 size_t Dbg::full_deoptimization_event_count_ = 0;
 size_t Dbg::delayed_full_undeoptimization_count_ = 0;
@@ -642,8 +650,6 @@
   CHECK(gRegistry == nullptr);
   gRegistry = new ObjectRegistry;
 
-  alloc_tracker_lock_ = new Mutex("AllocTracker lock");
-  deoptimization_lock_ = new Mutex("deoptimization lock", kDeoptimizationLock);
   // Init JDWP if the debugger is enabled. This may connect out to a
   // debugger, passively listen for a debugger, or block waiting for a
   // debugger.
@@ -677,10 +683,6 @@
   gJdwpState = nullptr;
   delete gRegistry;
   gRegistry = nullptr;
-  delete alloc_tracker_lock_;
-  alloc_tracker_lock_ = nullptr;
-  delete deoptimization_lock_;
-  deoptimization_lock_ = nullptr;
 }
 
 void Dbg::GcDidFinish() {
@@ -747,7 +749,7 @@
   }
 
   {
-    MutexLock mu(Thread::Current(), *deoptimization_lock_);
+    MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
     CHECK_EQ(deoptimization_requests_.size(), 0U);
     CHECK_EQ(full_deoptimization_event_count_, 0U);
     CHECK_EQ(delayed_full_undeoptimization_count_, 0U);
@@ -792,7 +794,7 @@
       // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
       // This prevents us from having any pending deoptimization request when the debugger attaches
       // to us again while no event has been requested yet.
-      MutexLock mu(Thread::Current(), *deoptimization_lock_);
+      MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
       deoptimization_requests_.clear();
       full_deoptimization_event_count_ = 0U;
       delayed_full_undeoptimization_count_ = 0U;
@@ -2922,7 +2924,7 @@
 }
 
 void Dbg::DelayFullUndeoptimization() {
-  MutexLock mu(Thread::Current(), *deoptimization_lock_);
+  MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
   ++delayed_full_undeoptimization_count_;
   DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
 }
@@ -2930,7 +2932,7 @@
 void Dbg::ProcessDelayedFullUndeoptimizations() {
   // TODO: avoid taking the lock twice (once here and once in ManageDeoptimization).
   {
-    MutexLock mu(Thread::Current(), *deoptimization_lock_);
+    MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
     while (delayed_full_undeoptimization_count_ > 0) {
       DeoptimizationRequest req;
       req.SetKind(DeoptimizationRequest::kFullUndeoptimization);
@@ -2947,7 +2949,7 @@
     // Nothing to do.
     return;
   }
-  MutexLock mu(Thread::Current(), *deoptimization_lock_);
+  MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
   RequestDeoptimizationLocked(req);
 }
 
@@ -3025,7 +3027,7 @@
   Thread* const self = Thread::Current();
   {
     // Avoid suspend/resume if there is no pending request.
-    MutexLock mu(self, *deoptimization_lock_);
+    MutexLock mu(self, *Locks::deoptimization_lock_);
     if (deoptimization_requests_.empty()) {
       return;
     }
@@ -3037,7 +3039,7 @@
   runtime->GetThreadList()->SuspendAll();
   const ThreadState old_state = self->SetStateUnsafe(kRunnable);
   {
-    MutexLock mu(self, *deoptimization_lock_);
+    MutexLock mu(self, *Locks::deoptimization_lock_);
     size_t req_index = 0;
     for (DeoptimizationRequest& request : deoptimization_requests_) {
       VLOG(jdwp) << "Process deoptimization request #" << req_index++;
@@ -4318,30 +4320,40 @@
   return kDefaultNumAllocRecords;
 }
 
-void Dbg::SetAllocTrackingEnabled(bool enabled) {
-  if (enabled) {
+void Dbg::SetAllocTrackingEnabled(bool enable) {
+  Thread* self = Thread::Current();
+  if (enable) {
     {
-      MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
-      if (recent_allocation_records_ == nullptr) {
-        alloc_record_max_ = GetAllocTrackerMax();
-        LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
-            << kMaxAllocRecordStackDepth << " frames, taking "
-            << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
-        alloc_record_head_ = alloc_record_count_ = 0;
-        recent_allocation_records_ = new AllocRecord[alloc_record_max_];
-        CHECK(recent_allocation_records_ != nullptr);
+      MutexLock mu(self, *Locks::alloc_tracker_lock_);
+      if (recent_allocation_records_ != nullptr) {
+        return;  // Already enabled, bail.
       }
+      alloc_record_max_ = GetAllocTrackerMax();
+      LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
+                << kMaxAllocRecordStackDepth << " frames, taking "
+                << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
+      DCHECK_EQ(alloc_record_head_, 0U);
+      DCHECK_EQ(alloc_record_count_, 0U);
+      recent_allocation_records_ = new AllocRecord[alloc_record_max_];
+      CHECK(recent_allocation_records_ != nullptr);
     }
     Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
   } else {
-    Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
     {
-      MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
+      ScopedObjectAccess soa(self);  // For type_cache_.Clear();
+      MutexLock mu(self, *Locks::alloc_tracker_lock_);
+      if (recent_allocation_records_ == nullptr) {
+        return;  // Already disabled, bail.
+      }
       LOG(INFO) << "Disabling alloc tracker";
       delete[] recent_allocation_records_;
       recent_allocation_records_ = nullptr;
+      alloc_record_head_ = 0;
+      alloc_record_count_ = 0;
       type_cache_.Clear();
     }
+    // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
+    Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
   }
 }
 
@@ -4381,8 +4393,9 @@
   Thread* self = Thread::Current();
   CHECK(self != nullptr);
 
-  MutexLock mu(self, *alloc_tracker_lock_);
+  MutexLock mu(self, *Locks::alloc_tracker_lock_);
   if (recent_allocation_records_ == nullptr) {
+    // In the process of shutting down recording, bail.
     return;
   }
 
@@ -4408,12 +4421,12 @@
 
 // Returns the index of the head element.
 //
-// We point at the most-recently-written record, so if gAllocRecordCount is 1
+// We point at the most-recently-written record, so if alloc_record_count_ is 1
 // we want to use the current element.  Take "head+1" and subtract count
 // from it.
 //
 // We need to handle underflow in our circular buffer, so we add
-// gAllocRecordMax and then mask it back down.
+// alloc_record_max_ and then mask it back down.
 size_t Dbg::HeadIndex() {
   return (Dbg::alloc_record_head_ + 1 + Dbg::alloc_record_max_ - Dbg::alloc_record_count_) &
       (Dbg::alloc_record_max_ - 1);
@@ -4421,7 +4434,7 @@
 
 void Dbg::DumpRecentAllocations() {
   ScopedObjectAccess soa(Thread::Current());
-  MutexLock mu(soa.Self(), *alloc_tracker_lock_);
+  MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
   if (recent_allocation_records_ == nullptr) {
     LOG(INFO) << "Not recording tracked allocations";
     return;
@@ -4430,7 +4443,8 @@
   // "i" is the head of the list.  We want to start at the end of the
   // list and move forward to the tail.
   size_t i = HeadIndex();
-  size_t count = alloc_record_count_;
+  const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+  uint16_t count = capped_count;
 
   LOG(INFO) << "Tracked allocations, (head=" << alloc_record_head_ << " count=" << count << ")";
   while (count--) {
@@ -4534,7 +4548,7 @@
  * followed by UTF-16 data.
  *
  * We send up 16-bit unsigned indexes into string tables.  In theory there
- * can be (kMaxAllocRecordStackDepth * gAllocRecordMax) unique strings in
+ * can be (kMaxAllocRecordStackDepth * alloc_record_max_) unique strings in
  * each table, but in practice there should be far fewer.
  *
  * The chief reason for using a string table here is to keep the size of
@@ -4554,7 +4568,7 @@
   Thread* self = Thread::Current();
   std::vector<uint8_t> bytes;
   {
-    MutexLock mu(self, *alloc_tracker_lock_);
+    MutexLock mu(self, *Locks::alloc_tracker_lock_);
     //
     // Part 1: generate string tables.
     //
@@ -4562,8 +4576,9 @@
     StringTable method_names;
     StringTable filenames;
 
-    int count = alloc_record_count_;
-    int idx = HeadIndex();
+    const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+    uint16_t count = capped_count;
+    size_t idx = HeadIndex();
     while (count--) {
       AllocRecord* record = &recent_allocation_records_[idx];
       std::string temp;
@@ -4580,7 +4595,7 @@
       idx = (idx + 1) & (alloc_record_max_ - 1);
     }
 
-    LOG(INFO) << "allocation records: " << alloc_record_count_;
+    LOG(INFO) << "allocation records: " << capped_count;
 
     //
     // Part 2: Generate the output and store it in the buffer.
@@ -4601,7 +4616,7 @@
     // (2b) number of class name strings
     // (2b) number of method name strings
     // (2b) number of source file name strings
-    JDWP::Append2BE(bytes, alloc_record_count_);
+    JDWP::Append2BE(bytes, capped_count);
     size_t string_table_offset = bytes.size();
     JDWP::Append4BE(bytes, 0);  // We'll patch this later...
     JDWP::Append2BE(bytes, class_names.Size());
@@ -4610,7 +4625,7 @@
 
     idx = HeadIndex();
     std::string temp;
-    for (count = alloc_record_count_; count != 0; --count) {
+    for (count = capped_count; count != 0; --count) {
       // For each entry:
       // (4b) total allocation size
       // (2b) thread id
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 52ae7a9..3e16288 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -192,9 +192,11 @@
   class TypeCache {
    public:
     // Returns a weak global for the input type. Deduplicates.
-    jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+                                                        Locks::alloc_tracker_lock_);
     // Clears the type cache and deletes all the weak global refs.
-    void Clear();
+    void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+                                       Locks::alloc_tracker_lock_);
 
    private:
     std::multimap<int32_t, jobject> objects_;
@@ -221,8 +223,8 @@
    */
   static void Connected();
   static void GoActive()
-      LOCKS_EXCLUDED(Locks::breakpoint_lock_, deoptimization_lock_, Locks::mutator_lock_);
-  static void Disconnected() LOCKS_EXCLUDED(deoptimization_lock_, Locks::mutator_lock_);
+      LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_);
+  static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_);
   static void Disposed();
 
   // Returns true if we're actually debugging with a real debugger, false if it's
@@ -493,20 +495,20 @@
 
   // Records deoptimization request in the queue.
   static void RequestDeoptimization(const DeoptimizationRequest& req)
-      LOCKS_EXCLUDED(deoptimization_lock_)
+      LOCKS_EXCLUDED(Locks::deoptimization_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Support delayed full undeoptimization requests. This is currently only used for single-step
   // events.
-  static void DelayFullUndeoptimization() LOCKS_EXCLUDED(deoptimization_lock_);
+  static void DelayFullUndeoptimization() LOCKS_EXCLUDED(Locks::deoptimization_lock_);
   static void ProcessDelayedFullUndeoptimizations()
-      LOCKS_EXCLUDED(deoptimization_lock_)
+      LOCKS_EXCLUDED(Locks::deoptimization_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Manage deoptimization after updating JDWP events list. Suspends all threads, processes each
   // request and finally resumes all threads.
   static void ManageDeoptimization()
-      LOCKS_EXCLUDED(deoptimization_lock_)
+      LOCKS_EXCLUDED(Locks::deoptimization_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Breakpoints.
@@ -560,17 +562,17 @@
    * Recent allocation tracking support.
    */
   static void RecordAllocation(mirror::Class* type, size_t byte_count)
-      LOCKS_EXCLUDED(alloc_tracker_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(alloc_tracker_lock_);
+  static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
   static bool IsAllocTrackingEnabled() {
     return recent_allocation_records_ != nullptr;
   }
   static jbyteArray GetRecentAllocations()
-      LOCKS_EXCLUDED(alloc_tracker_lock_)
+      LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(alloc_tracker_lock_);
-  static void DumpRecentAllocations() LOCKS_EXCLUDED(alloc_tracker_lock_);
+  static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+  static void DumpRecentAllocations() LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
 
   enum HpifWhen {
     HPIF_WHEN_NEVER = 0,
@@ -596,10 +598,6 @@
   static void DdmSendHeapSegments(bool native)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static TypeCache* GetTypeCache() {
-    return &type_cache_;
-  }
-
  private:
   static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void PostThreadStartOrStop(Thread*, uint32_t)
@@ -617,52 +615,47 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static void RequestDeoptimizationLocked(const DeoptimizationRequest& req)
-      EXCLUSIVE_LOCKS_REQUIRED(deoptimization_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::deoptimization_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static Mutex* alloc_tracker_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-
-  static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(alloc_tracker_lock_);
-  static size_t alloc_record_max_ GUARDED_BY(alloc_tracker_lock_);
-  static size_t alloc_record_head_ GUARDED_BY(alloc_tracker_lock_);
-  static size_t alloc_record_count_ GUARDED_BY(alloc_tracker_lock_);
-
-  // Guards deoptimization requests.
-  // TODO rename to instrumentation_update_lock.
-  static Mutex* deoptimization_lock_ ACQUIRED_AFTER(Locks::breakpoint_lock_);
+  static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(Locks::alloc_tracker_lock_);
+  static size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
+  static size_t alloc_record_head_ GUARDED_BY(Locks::alloc_tracker_lock_);
+  static size_t alloc_record_count_ GUARDED_BY(Locks::alloc_tracker_lock_);
 
   // Deoptimization requests to be processed each time the event list is updated. This is used when
   // registering and unregistering events so we do not deoptimize while holding the event list
   // lock.
   // TODO rename to instrumentation_requests.
-  static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(deoptimization_lock_);
+  static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(Locks::deoptimization_lock_);
 
   // Count the number of events requiring full deoptimization. When the counter is > 0, everything
   // is deoptimized, otherwise everything is undeoptimized.
   // Note: we fully deoptimize on the first event only (when the counter is set to 1). We fully
   // undeoptimize when the last event is unregistered (when the counter is set to 0).
-  static size_t full_deoptimization_event_count_ GUARDED_BY(deoptimization_lock_);
+  static size_t full_deoptimization_event_count_ GUARDED_BY(Locks::deoptimization_lock_);
 
   // Count the number of full undeoptimization requests delayed to next resume or end of debug
   // session.
-  static size_t delayed_full_undeoptimization_count_ GUARDED_BY(deoptimization_lock_);
+  static size_t delayed_full_undeoptimization_count_ GUARDED_BY(Locks::deoptimization_lock_);
 
   static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
 
   // Weak global type cache, TODO improve this.
-  static TypeCache type_cache_;
+  static TypeCache type_cache_ GUARDED_BY(Locks::alloc_tracker_lock_);
 
   // Instrumentation event reference counters.
   // TODO we could use an array instead of having all these dedicated counters. Instrumentation
   // events are bits of a mask so we could convert them to array index.
-  static size_t dex_pc_change_event_ref_count_ GUARDED_BY(deoptimization_lock_);
-  static size_t method_enter_event_ref_count_ GUARDED_BY(deoptimization_lock_);
-  static size_t method_exit_event_ref_count_ GUARDED_BY(deoptimization_lock_);
-  static size_t field_read_event_ref_count_ GUARDED_BY(deoptimization_lock_);
-  static size_t field_write_event_ref_count_ GUARDED_BY(deoptimization_lock_);
-  static size_t exception_catch_event_ref_count_ GUARDED_BY(deoptimization_lock_);
+  static size_t dex_pc_change_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+  static size_t method_enter_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+  static size_t method_exit_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+  static size_t field_read_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+  static size_t field_write_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+  static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
   static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
 
+  friend class AllocRecord;  // For type_cache_ with proper annotalysis.
   DISALLOW_COPY_AND_ASSIGN(Dbg);
 };
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 494781a..2048160 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -120,7 +120,7 @@
       desired_collector_type_(foreground_collector_type_),
       heap_trim_request_lock_(nullptr),
       last_trim_time_(0),
-      last_heap_transition_time_(0),
+      heap_transition_or_trim_target_time_(0),
       heap_trim_request_pending_(false),
       parallel_gc_threads_(parallel_gc_threads),
       conc_gc_threads_(conc_gc_threads),
@@ -916,6 +916,35 @@
 }
 
 void Heap::DoPendingTransitionOrTrim() {
+  Thread* self = Thread::Current();
+  CollectorType desired_collector_type;
+  // Wait until we reach the desired transition time.
+  while (true) {
+    uint64_t wait_time;
+    {
+      MutexLock mu(self, *heap_trim_request_lock_);
+      desired_collector_type = desired_collector_type_;
+      uint64_t current_time = NanoTime();
+      if (current_time >= heap_transition_or_trim_target_time_) {
+        break;
+      }
+      wait_time = heap_transition_or_trim_target_time_ - current_time;
+    }
+    ScopedThreadStateChange tsc(self, kSleeping);
+    usleep(wait_time / 1000);  // Usleep takes microseconds.
+  }
+  // Launch homogeneous space compaction if it is desired.
+  if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
+    if (!CareAboutPauseTimes()) {
+      PerformHomogeneousSpaceCompact();
+    }
+    // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
+    desired_collector_type = collector_type_;
+    return;
+  }
+  // Transition the collector if the desired collector type is not the same as the current
+  // collector type.
+  TransitionCollector(desired_collector_type);
   if (!CareAboutPauseTimes()) {
     // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
     // about pauses.
@@ -927,23 +956,7 @@
         << PrettyDuration(NanoTime() - start_time);
     runtime->GetThreadList()->ResumeAll();
   }
-  if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) {
-    // Launch homogeneous space compaction if it is desired.
-    if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) {
-      if (!CareAboutPauseTimes()) {
-        PerformHomogeneousSpaceCompact();
-        last_heap_transition_time_ = NanoTime();
-      }
-      desired_collector_type_ = collector_type_;
-    } else {
-      // Transition the collector if the desired collector type is not the same as the current
-      // collector type.
-      TransitionCollector(desired_collector_type_);
-      last_heap_transition_time_ = NanoTime();
-    }
-  }
-  // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may
-  // trim the native heap and dlmalloc spaces.
+  // Do a heap trim if it is needed.
   Trim();
 }
 
@@ -2977,6 +2990,8 @@
     if (desired_collector_type_ == desired_collector_type) {
       return;
     }
+    heap_transition_or_trim_target_time_ =
+        std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
     desired_collector_type_ = desired_collector_type;
   }
   SignalHeapTrimDaemon(self);
@@ -2998,7 +3013,10 @@
 
   Thread* self = Thread::Current();
   Runtime* runtime = Runtime::Current();
-  if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) {
+  if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
+      runtime->IsZygote()) {
+    // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap
+    // trimmer daemon. b/17310019
     // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time)
     // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check
     // as we don't hold the lock while requesting the trim).
@@ -3012,6 +3030,10 @@
       return;
     }
     heap_trim_request_pending_ = true;
+    uint64_t current_time = NanoTime();
+    if (heap_transition_or_trim_target_time_ < current_time) {
+      heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
+    }
   }
   // Notify the daemon thread which will actually do the heap trim.
   SignalHeapTrimDaemon(self);
@@ -3064,8 +3086,6 @@
   }
   env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
                             WellKnownClasses::java_lang_System_runFinalization);
-  env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
-                            WellKnownClasses::java_lang_System_runFinalization);
 }
 
 void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 3bfa748..9742277 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -830,8 +830,8 @@
   Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   // When we want to perform the next heap trim (nano seconds).
   uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
-  // When we last performed a heap transition or hspace compact.
-  uint64_t last_heap_transition_time_;
+  // When we want to perform the next heap transition (nano seconds) or heap trim.
+  uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
   // If we have a heap trim request pending.
   bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
 
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index c50c703..ae17070 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -271,6 +271,9 @@
 }
 
 inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+  if (IsOptimized()) {
+    LOG(FATAL) << "Unimplemented vmap table for optimized compiler";
+  }
   DCHECK(code_pointer != nullptr);
   DCHECK(code_pointer == GetQuickOatCodePointer());
   uint32_t offset =
@@ -281,6 +284,17 @@
   return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
 }
 
+inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) {
+  DCHECK(IsOptimized());
+  const void* code_pointer = GetQuickOatCodePointer();
+  DCHECK(code_pointer != nullptr);
+  uint32_t offset =
+      reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
+  const void* data = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(code_pointer) - offset);
+  CodeInfo code_info(data);
+  return code_info.GetStackMapForNativePcOffset(native_pc_offset);
+}
+
 inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
   DCHECK(!Runtime::Current()->IsStarted());
   SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index ebd5bd5..d37aa57 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -25,6 +25,7 @@
 #include "object_callbacks.h"
 #include "quick/quick_method_frame_info.h"
 #include "read_barrier_option.h"
+#include "stack_map.h"
 
 namespace art {
 
@@ -150,6 +151,13 @@
     SetAccessFlags(GetAccessFlags() | kAccPreverified);
   }
 
+  bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Temporary solution for detecting if a method has been optimized: the compiler
+    // does not create a GC map. Instead, the vmap table contains the stack map
+    // (as in stack_map.h).
+    return (GetEntryPointFromQuickCompiledCode() != nullptr) && (GetNativeGcMap() == nullptr);
+  }
+
   bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return (GetAccessFlags() & kAccPortableCompiled) != 0;
   }
@@ -340,6 +348,8 @@
   const uint8_t* GetVmapTable(const void* code_pointer)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
   }
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index f3594e4..ff2ad89 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -32,6 +32,9 @@
   static constexpr uint32_t InstanceSize() {
     return sizeof(ClassLoader);
   }
+  ClassLoader* GetParent() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, parent_));
+  }
 
  private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d0060e..b8b10d2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -115,18 +115,22 @@
 mirror::Object* StackVisitor::GetThisObject() const {
   mirror::ArtMethod* m = GetMethod();
   if (m->IsStatic()) {
-    return NULL;
+    return nullptr;
   } else if (m->IsNative()) {
-    if (cur_quick_frame_ != NULL) {
+    if (cur_quick_frame_ != nullptr) {
       HandleScope* hs = reinterpret_cast<HandleScope*>(
           reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes());
       return hs->GetReference(0);
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
+  } else if (m->IsOptimized()) {
+    // TODO: Implement, currently only used for exceptions when jdwp is enabled.
+    LOG(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
+    return nullptr;
   } else {
     const DexFile::CodeItem* code_item = m->GetCodeItem();
-    if (code_item == NULL) {
+    if (code_item == nullptr) {
       UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
           << PrettyMethod(m);
       return nullptr;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7d3a48f..9b49d31 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -64,9 +64,9 @@
 
   MemoryRegion region_;
 
-  template<typename T> friend class CodeInfo;
-  template<typename T> friend class StackMap;
-  template<typename T> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMap;
+  friend class StackMapStream;
 };
 
 /**
@@ -77,13 +77,15 @@
  * The location_kind for a Dex register can either be:
  * - Constant: register_value holds the constant,
  * - Stack: register_value holds the stack offset,
- * - Register: register_value holds the register number.
+ * - Register: register_value holds the physical register number.
+ * - None: the register has no location yet, meaning it has not been set.
  */
 class DexRegisterMap {
  public:
   explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
 
   enum LocationKind {
+    kNone,
     kInStack,
     kInRegister,
     kConstant
@@ -114,8 +116,8 @@
 
   MemoryRegion region_;
 
-  template <typename T> friend class CodeInfo;
-  template <typename T> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMapStream;
 };
 
 /**
@@ -127,12 +129,11 @@
  * - Knowing the values of dex registers.
  *
  * The information is of the form:
- * [dex_pc, native_pc, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
+ * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
  *
  * Note that register_mask is fixed size, but stack_mask is variable size, depending on the
  * stack size of a method.
  */
-template <typename T>
 class StackMap {
  public:
   explicit StackMap(MemoryRegion region) : region_(region) {}
@@ -145,12 +146,12 @@
     region_.Store<uint32_t>(kDexPcOffset, dex_pc);
   }
 
-  T GetNativePc() const {
-    return region_.Load<T>(kNativePcOffset);
+  uint32_t GetNativePcOffset() const {
+    return region_.Load<uint32_t>(kNativePcOffsetOffset);
   }
 
-  void SetNativePc(T native_pc) {
-    return region_.Store<T>(kNativePcOffset, native_pc);
+  void SetNativePcOffset(uint32_t native_pc_offset) {
+    return region_.Store<uint32_t>(kNativePcOffsetOffset, native_pc_offset);
   }
 
   uint32_t GetDexRegisterMapOffset() const {
@@ -199,8 +200,8 @@
 
  private:
   static constexpr int kDexPcOffset = 0;
-  static constexpr int kNativePcOffset = kDexPcOffset + sizeof(uint32_t);
-  static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffset + sizeof(T);
+  static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
+  static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffsetOffset + sizeof(uint32_t);
   static constexpr int kInlineDescriptorOffsetOffset =
       kDexRegisterMapOffsetOffset + sizeof(uint32_t);
   static constexpr int kRegisterMaskOffset = kInlineDescriptorOffsetOffset + sizeof(uint32_t);
@@ -211,24 +212,36 @@
 
   MemoryRegion region_;
 
-  template <typename U> friend class CodeInfo;
-  template <typename U> friend class StackMapStream;
+  friend class CodeInfo;
+  friend class StackMapStream;
 };
 
 
 /**
  * Wrapper around all compiler information collected for a method.
  * The information is of the form:
- * [number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
+ * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
  */
-template <typename T>
 class CodeInfo {
  public:
   explicit CodeInfo(MemoryRegion region) : region_(region) {}
 
-  StackMap<T> GetStackMapAt(size_t i) const {
+  explicit CodeInfo(const void* data) {
+    uint32_t size = reinterpret_cast<const uint32_t*>(data)[0];
+    region_ = MemoryRegion(const_cast<void*>(data), size);
+  }
+
+  StackMap GetStackMapAt(size_t i) const {
     size_t size = StackMapSize();
-    return StackMap<T>(GetStackMaps().Subregion(i * size, size));
+    return StackMap(GetStackMaps().Subregion(i * size, size));
+  }
+
+  uint32_t GetOverallSize() const {
+    return region_.Load<uint32_t>(kOverallSizeOffset);
+  }
+
+  void SetOverallSize(uint32_t size) {
+    region_.Store<uint32_t>(kOverallSizeOffset, size);
   }
 
   uint32_t GetStackMaskSize() const {
@@ -248,47 +261,48 @@
   }
 
   size_t StackMapSize() const {
-    return StackMap<T>::kFixedSize + GetStackMaskSize();
+    return StackMap::kFixedSize + GetStackMaskSize();
   }
 
-  DexRegisterMap GetDexRegisterMapOf(StackMap<T> stack_map, uint32_t number_of_dex_registers) {
+  DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
     uint32_t offset = stack_map.GetDexRegisterMapOffset();
     return DexRegisterMap(region_.Subregion(offset,
         DexRegisterMap::kFixedSize + number_of_dex_registers * DexRegisterMap::SingleEntrySize()));
   }
 
-  InlineInfo GetInlineInfoOf(StackMap<T> stack_map) {
+  InlineInfo GetInlineInfoOf(StackMap stack_map) {
     uint32_t offset = stack_map.GetInlineDescriptorOffset();
     uint8_t depth = region_.Load<uint8_t>(offset);
     return InlineInfo(region_.Subregion(offset,
         InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
   }
 
-  StackMap<T> GetStackMapForDexPc(uint32_t dex_pc) {
+  StackMap GetStackMapForDexPc(uint32_t dex_pc) {
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
-      StackMap<T> stack_map = GetStackMapAt(i);
+      StackMap stack_map = GetStackMapAt(i);
       if (stack_map.GetDexPc() == dex_pc) {
         return stack_map;
       }
     }
     LOG(FATAL) << "Unreachable";
-    return StackMap<T>(MemoryRegion());
+    return StackMap(MemoryRegion());
   }
 
-  StackMap<T> GetStackMapForNativePc(T native_pc) {
+  StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) {
     // TODO: stack maps are sorted by native pc, we can do a binary search.
     for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
-      StackMap<T> stack_map = GetStackMapAt(i);
-      if (stack_map.GetNativePc() == native_pc) {
+      StackMap stack_map = GetStackMapAt(i);
+      if (stack_map.GetNativePcOffset() == native_pc_offset) {
         return stack_map;
       }
     }
     LOG(FATAL) << "Unreachable";
-    return StackMap<T>(MemoryRegion());
+    return StackMap(MemoryRegion());
   }
 
  private:
-  static constexpr int kNumberOfStackMapsOffset = 0;
+  static constexpr int kOverallSizeOffset = 0;
+  static constexpr int kNumberOfStackMapsOffset = kOverallSizeOffset + sizeof(uint32_t);
   static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
   static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
 
@@ -299,7 +313,7 @@
   }
 
   MemoryRegion region_;
-  template<typename U> friend class StackMapStream;
+  friend class StackMapStream;
 };
 
 }  // namespace art
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e323473..6e3e9c1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2070,48 +2070,72 @@
 
     // Process register map (which native and runtime methods don't have)
     if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
-      const uint8_t* native_gc_map = m->GetNativeGcMap();
-      CHECK(native_gc_map != nullptr) << PrettyMethod(m);
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be nullptr or how would we compile its instructions?
-      NativePcOffsetToReferenceMap map(native_gc_map);
-      size_t num_regs = std::min(map.RegWidth() * 8,
-                                 static_cast<size_t>(code_item->registers_size_));
-      if (num_regs > 0) {
+      if (m->IsOptimized()) {
         Runtime* runtime = Runtime::Current();
         const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
         uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
-        const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
-        DCHECK(reg_bitmap != nullptr);
-        const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
-        const VmapTable vmap_table(m->GetVmapTable(code_pointer));
-        QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-        // For all dex registers in the bitmap
-        StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
-        DCHECK(cur_quick_frame != nullptr);
-        for (size_t reg = 0; reg < num_regs; ++reg) {
-          // Does this register hold a reference?
-          if (TestBitmap(reg, reg_bitmap)) {
-            uint32_t vmap_offset;
-            if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
-              int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
-                                                        kReferenceVReg);
-              // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
-              mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
-              if (*ref_addr != nullptr) {
-                visitor_(ref_addr, reg, this);
+        StackMap map = m->GetStackMap(native_pc_offset);
+        MemoryRegion mask = map.GetStackMask();
+        for (size_t i = 0; i < mask.size_in_bits(); ++i) {
+          if (mask.LoadBit(i)) {
+            StackReference<mirror::Object>* ref_addr =
+                  reinterpret_cast<StackReference<mirror::Object>*>(cur_quick_frame) + i;
+            mirror::Object* ref = ref_addr->AsMirrorPtr();
+            if (ref != nullptr) {
+              mirror::Object* new_ref = ref;
+              visitor_(&new_ref, -1, this);
+              if (ref != new_ref) {
+                ref_addr->Assign(new_ref);
               }
-            } else {
-              StackReference<mirror::Object>* ref_addr =
-                  reinterpret_cast<StackReference<mirror::Object>*>(
-                      GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
-                                  frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
-              mirror::Object* ref = ref_addr->AsMirrorPtr();
-              if (ref != nullptr) {
-                mirror::Object* new_ref = ref;
-                visitor_(&new_ref, reg, this);
-                if (ref != new_ref) {
-                  ref_addr->Assign(new_ref);
+            }
+          }
+        }
+      } else {
+        const uint8_t* native_gc_map = m->GetNativeGcMap();
+        CHECK(native_gc_map != nullptr) << PrettyMethod(m);
+        const DexFile::CodeItem* code_item = m->GetCodeItem();
+        // Can't be nullptr or how would we compile its instructions?
+        DCHECK(code_item != nullptr) << PrettyMethod(m);
+        NativePcOffsetToReferenceMap map(native_gc_map);
+        size_t num_regs = std::min(map.RegWidth() * 8,
+                                   static_cast<size_t>(code_item->registers_size_));
+        if (num_regs > 0) {
+          Runtime* runtime = Runtime::Current();
+          const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+          uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
+          const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
+          DCHECK(reg_bitmap != nullptr);
+          const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
+          const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+          QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+          // For all dex registers in the bitmap
+          StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
+          DCHECK(cur_quick_frame != nullptr);
+          for (size_t reg = 0; reg < num_regs; ++reg) {
+            // Does this register hold a reference?
+            if (TestBitmap(reg, reg_bitmap)) {
+              uint32_t vmap_offset;
+              if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
+                int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
+                                                          kReferenceVReg);
+                // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
+                mirror::Object** ref_addr =
+                    reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
+                if (*ref_addr != nullptr) {
+                  visitor_(ref_addr, reg, this);
+                }
+              } else {
+                StackReference<mirror::Object>* ref_addr =
+                    reinterpret_cast<StackReference<mirror::Object>*>(
+                        GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
+                                    frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
+                mirror::Object* ref = ref_addr->AsMirrorPtr();
+                if (ref != nullptr) {
+                  mirror::Object* new_ref = ref;
+                  visitor_(&new_ref, reg, this);
+                  if (ref != new_ref) {
+                    ref_addr->Assign(new_ref);
+                  }
                 }
               }
             }
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3a6a72b..7068a4d 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -26,7 +26,11 @@
 namespace art {
 
 jclass WellKnownClasses::com_android_dex_Dex;
+jclass WellKnownClasses::dalvik_system_DexFile;
+jclass WellKnownClasses::dalvik_system_DexPathList;
+jclass WellKnownClasses::dalvik_system_DexPathList$Element;
 jclass WellKnownClasses::dalvik_system_PathClassLoader;
+jclass WellKnownClasses::java_lang_BootClassLoader;
 jclass WellKnownClasses::java_lang_ClassLoader;
 jclass WellKnownClasses::java_lang_ClassNotFoundException;
 jclass WellKnownClasses::java_lang_Daemons;
@@ -79,6 +83,10 @@
 jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
 jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
 
+jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
+jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
+jfieldID WellKnownClasses::dalvik_system_DexPathList$Element_dexFile;
 jfieldID WellKnownClasses::java_lang_Thread_daemon;
 jfieldID WellKnownClasses::java_lang_Thread_group;
 jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -131,7 +139,11 @@
 
 void WellKnownClasses::Init(JNIEnv* env) {
   com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
+  dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
+  dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
+  dalvik_system_DexPathList$Element = CacheClass(env, "dalvik/system/DexPathList$Element");
   dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
+  java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
   java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
   java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
   java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
@@ -179,6 +191,10 @@
   org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
   org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
 
+  dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "J");
+  dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+  dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
+  dalvik_system_DexPathList$Element_dexFile = CacheField(env, dalvik_system_DexPathList$Element, false, "dexFile", "Ldalvik/system/DexFile;");
   java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
   java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
   java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 7639f50..b10106c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -40,7 +40,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   static jclass com_android_dex_Dex;
+  static jclass dalvik_system_DexFile;
+  static jclass dalvik_system_DexPathList;
+  static jclass dalvik_system_DexPathList$Element;
   static jclass dalvik_system_PathClassLoader;
+  static jclass java_lang_BootClassLoader;
   static jclass java_lang_ClassLoader;
   static jclass java_lang_ClassNotFoundException;
   static jclass java_lang_Daemons;
@@ -93,6 +97,10 @@
   static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
   static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
 
+  static jfieldID dalvik_system_DexFile_cookie;
+  static jfieldID dalvik_system_DexPathList_dexElements;
+  static jfieldID dalvik_system_DexPathList$Element_dexFile;
+  static jfieldID dalvik_system_PathClassLoader_pathList;
   static jfieldID java_lang_reflect_AbstractMethod_artMethod;
   static jfieldID java_lang_reflect_Field_artField;
   static jfieldID java_lang_reflect_Proxy_h;
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index e69de29..49d9cc0 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -0,0 +1,29 @@
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+RUNNING super object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, super class, super nonstatic
+Super.<init>
+RUNNING super object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, super class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, sub nonstatic
+Subclass.nonstaticMethod
+PASSED sub object, sub class, sub nonstatic
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f5a1d65..6fc4484 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -353,3 +353,198 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
   PthreadHelper(&testShallowGetStackClass2);
 }
+
+class JniCallNonvirtualVoidMethodTest {
+ public:
+  explicit JniCallNonvirtualVoidMethodTest(JNIEnv* env)
+      : env_(env),
+        check_jni_ri_(true),
+        check_jni_android_(true),
+        super_(GetClass("JniCallNonvirtualTest")),
+        sub_(GetClass("JniCallNonvirtualTestSubclass")),
+        super_constructor_(GetMethodID(super_, true, "<init>")),
+        super_static_(GetMethodID(super_, false, "staticMethod")),
+        super_nonstatic_(GetMethodID(super_, true, "nonstaticMethod")),
+        sub_constructor_(GetMethodID(sub_, true, "<init>")),
+        sub_static_(GetMethodID(sub_, false, "staticMethod")),
+        sub_nonstatic_(GetMethodID(sub_, true, "nonstaticMethod")),
+        super_field_(GetFieldID(super_, "nonstaticMethodSuperCalled")),
+        sub_field_(GetFieldID(super_, "nonstaticMethodSubCalled")) {}
+
+  void Test() {
+    TestStaticCallNonvirtualMethod();
+    TestNewObject();
+    TestnonstaticCallNonvirtualMethod();
+  }
+
+  JNIEnv* const env_;
+
+  bool const check_jni_ri_;
+  bool const check_jni_android_;
+
+  jclass const super_;
+  jclass const sub_;
+
+  jmethodID const super_constructor_;
+  jmethodID const super_static_;
+  jmethodID const super_nonstatic_;
+  jmethodID const sub_constructor_;
+  jmethodID const sub_static_;
+  jmethodID const sub_nonstatic_;
+
+  jfieldID const super_field_;
+  jfieldID const sub_field_;
+
+ private:
+  jclass GetClass(const char* class_name) {
+    jclass c = env_->FindClass(class_name);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(!env_->ExceptionCheck());
+    assert(c != nullptr);
+    return c;
+  }
+
+  jmethodID GetMethodID(jclass c, bool nonstatic, const char* method_name) {
+    jmethodID m = ((nonstatic) ?
+                   env_->GetMethodID(c, method_name, "()V") :
+                   env_->GetStaticMethodID(c, method_name, "()V"));
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(m != nullptr);
+    return m;
+  }
+
+  jobject CallConstructor(jclass c, jmethodID m) {
+    jobject o = env_->NewObject(c, m);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(o != nullptr);
+    return o;
+  }
+
+  void CallMethod(jobject o, jclass c, jmethodID m, bool nonstatic, const char* test_case) {
+    printf("RUNNING %s\n", test_case);
+    env_->CallNonvirtualVoidMethod(o, c, m);
+    bool exception_check = env_->ExceptionCheck();
+    if (c == nullptr || !nonstatic) {
+      if (!exception_check) {
+        printf("FAILED %s due to missing exception\n", test_case);
+        env_->FatalError("Expected NullPointerException with null jclass");
+      }
+      env_->ExceptionClear();
+    } else if (exception_check) {
+      printf("FAILED %s due to pending exception\n", test_case);
+      env_->ExceptionDescribe();
+      env_->FatalError(test_case);
+    }
+    printf("PASSED %s\n", test_case);
+  }
+
+  jfieldID GetFieldID(jclass c, const char* field_name) {
+    jfieldID m = env_->GetFieldID(c, field_name, "Z");
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    assert(m != nullptr);
+    return m;
+  }
+
+  jboolean GetBooleanField(jobject o, jfieldID f) {
+    jboolean b = env_->GetBooleanField(o, f);
+    if (env_->ExceptionCheck()) {
+      env_->ExceptionDescribe();
+      env_->FatalError(__FUNCTION__);
+    }
+    return b;
+  }
+
+  void TestStaticCallNonvirtualMethod() {
+    if (!check_jni_ri_&& !check_jni_android_) {
+      CallMethod(nullptr, nullptr, super_static_, false, "null object, null class, super static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, super_, super_static_, false, "null object, super class, super static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, sub_, super_static_, false, "null object, sub class, super static");
+    }
+
+    if (!check_jni_ri_ && !check_jni_android_) {
+      CallMethod(nullptr, nullptr, sub_static_, false, "null object, null class, sub static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, sub_, sub_static_, false, "null object, super class, sub static");
+    }
+    if (!check_jni_android_) {
+      CallMethod(nullptr, super_, sub_static_, false, "null object, super class, sub static");
+    }
+  }
+
+  void TestNewObject() {
+    jobject super_super = CallConstructor(super_, super_constructor_);
+    jobject super_sub = CallConstructor(super_, sub_constructor_);
+    jobject sub_super = CallConstructor(sub_, super_constructor_);
+    jobject sub_sub = CallConstructor(sub_, sub_constructor_);
+
+    assert(env_->IsInstanceOf(super_super, super_));
+    assert(!env_->IsInstanceOf(super_super, sub_));
+
+    // Note that even though we called (and ran) the subclass
+    // constructor, we are not the subclass.
+    assert(env_->IsInstanceOf(super_sub, super_));
+    assert(!env_->IsInstanceOf(super_sub, sub_));
+
+    // Note that even though we called the superclass constructor, we
+    // are still the subclass.
+    assert(env_->IsInstanceOf(sub_super, super_));
+    assert(env_->IsInstanceOf(sub_super, sub_));
+
+    assert(env_->IsInstanceOf(sub_sub, super_));
+    assert(env_->IsInstanceOf(sub_sub, sub_));
+  }
+
+  void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
+    if (check_jni_android_) {
+      if (super_object && !super_method) {
+        return;  // We don't allow a call with sub class method on the super class instance.
+      }
+      if (super_class && !super_method) {
+        return;  // We don't allow a call with the sub class method with the super class argument.
+      }
+    }
+    jobject o = ((super_object) ?
+                 CallConstructor(super_, super_constructor_) :
+                 CallConstructor(sub_, sub_constructor_));
+    jclass c = (super_class) ? super_ : sub_;
+    jmethodID m = (super_method) ? super_nonstatic_ : sub_nonstatic_;
+    CallMethod(o, c, m, true, test_case);
+    jboolean super_field = GetBooleanField(o, super_field_);
+    jboolean sub_field = GetBooleanField(o, sub_field_);
+    assert(super_field == super_method);
+    assert(sub_field != super_method);
+  }
+
+  void TestnonstaticCallNonvirtualMethod() {
+    TestnonstaticCallNonvirtualMethod(true, true, true, "super object, super class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, false, true, "super object, sub class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, false, false, "super object, sub class, sub nonstatic");
+    TestnonstaticCallNonvirtualMethod(true, true, false, "super object, super class, sub nonstatic");
+
+    TestnonstaticCallNonvirtualMethod(false, true, true, "sub object, super class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, false, true, "sub object, sub class, super nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, false, false, "sub object, sub class, sub nonstatic");
+    TestnonstaticCallNonvirtualMethod(false, true, false, "sub object, super class, sub nonstatic");
+  }
+};
+
+extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
+  JniCallNonvirtualVoidMethodTest(env).Test();
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5884bc0..8e92010 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -32,6 +32,7 @@
         testIsAssignableFromOnPrimitiveTypes();
         testShallowGetCallingClassLoader();
         testShallowGetStackClass2();
+        testCallNonvirtual();
     }
 
     private static native void testFindClassOnAttachedNativeThread();
@@ -94,7 +95,7 @@
 
     // Test sign-extension for values < 32b
 
-    native static byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
+    static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
         byte b8, byte b9, byte b10);
 
     private static void testByteMethod() {
@@ -109,7 +110,7 @@
       }
     }
 
-    native static short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
+    private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
         short s8, short s9, short s10);
 
     private static void testShortMethod() {
@@ -126,7 +127,7 @@
 
     // Test zero-extension for values < 32b
 
-    native static boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
+    private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
         boolean b8, boolean b9, boolean b10);
 
     private static void testBooleanMethod() {
@@ -139,7 +140,7 @@
       }
     }
 
-    native static char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
+    private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
         char c8, char c9, char c10);
 
     private static void testCharMethod() {
@@ -168,17 +169,55 @@
       }
     }
 
-    native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
+    private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
 
-    static void testShallowGetCallingClassLoader() {
+    private static void testShallowGetCallingClassLoader() {
         nativeTestShallowGetCallingClassLoader();
     }
 
-    native static void nativeTestShallowGetCallingClassLoader();
+    private native static void nativeTestShallowGetCallingClassLoader();
 
-    static void testShallowGetStackClass2() {
+    private static void testShallowGetStackClass2() {
         nativeTestShallowGetStackClass2();
     }
 
-    native static void nativeTestShallowGetStackClass2();
+    private static native void nativeTestShallowGetStackClass2();
+
+    private static native void testCallNonvirtual();
+}
+
+class JniCallNonvirtualTest {
+    public boolean nonstaticMethodSuperCalled = false;
+    public boolean nonstaticMethodSubCalled = false;
+
+    private static native void testCallNonvirtual();
+
+    public JniCallNonvirtualTest() {
+        System.out.println("Super.<init>");
+    }
+
+    public static void staticMethod() {
+        System.out.println("Super.staticMethod");
+    }
+
+    public void nonstaticMethod() {
+        System.out.println("Super.nonstaticMethod");
+        nonstaticMethodSuperCalled = true;
+    }
+}
+
+class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
+
+    public JniCallNonvirtualTestSubclass() {
+        System.out.println("Subclass.<init>");
+    }
+
+    public static void staticMethod() {
+        System.out.println("Subclass.staticMethod");
+    }
+
+    public void nonstaticMethod() {
+        System.out.println("Subclass.nonstaticMethod");
+        nonstaticMethodSubCalled = true;
+    }
 }
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index a2a74fc..36fa5f8 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -11,3 +11,4 @@
 sleep
 reborn: [FinalizerTest message=nothing, finalized=false]
 wimp: null
+Finalized 1024 / 1024
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 328425f..390472d 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,39 @@
 
         System.out.println("reborn: " + FinalizerTest.mReborn);
         System.out.println("wimp: " + wimpString(wimp));
+        // Test runFinalization with multiple objects.
+        runFinalizationTest();
+    }
+
+    static class FinalizeCounter {
+      private static Object finalizeLock = new Object();
+      private static volatile int finalizeCount = 0;
+      private int index;
+      static int getCount() {
+        return finalizeCount;
+      }
+      FinalizeCounter(int index) {
+        this.index = index;
+      }
+      protected void finalize() {
+        synchronized(finalizeLock) {
+          ++finalizeCount;
+        }
+      }
+    }
+
+    private static void runFinalizationTest() {
+      int count = 1024;
+      Object[] objs = new Object[count];
+      for (int i = 0; i < count; ++i) {
+        objs[i] = new FinalizeCounter(i);
+      }
+      for (int i = 0; i < count; ++i) {
+        objs[i] = null;
+      }
+      System.gc();
+      System.runFinalization();
+      System.out.println("Finalized " + FinalizeCounter.getCount() + " / "  + count);
     }
 
     public static class FinalizerTest {
diff --git a/test/098-ddmc/expected.txt b/test/098-ddmc/expected.txt
new file mode 100644
index 0000000..f8cda4c
--- /dev/null
+++ b/test/098-ddmc/expected.txt
@@ -0,0 +1,23 @@
+Confirm empty
+empty=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm enable
+status=false
+status=true
+Capture some allocations (note just this causes allocations)
+before > 0=true
+Confirm when we overflow, we don't roll over to zero. b/17392248
+before < overflowAllocations=true
+after > before=true
+after.numberOfEntries=65535
+Disable and confirm back to empty
+status=false
+reset=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm we can disable twice in a row
+status=false
+status=false
+Confirm we can reenable twice in a row without losing allocations
+status=true
+status=true
+second > first =true
+Goodbye
+goodbye=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
diff --git a/test/098-ddmc/info.txt b/test/098-ddmc/info.txt
new file mode 100644
index 0000000..39d26db
--- /dev/null
+++ b/test/098-ddmc/info.txt
@@ -0,0 +1 @@
+Tests of private org.apache.harmony.dalvik.ddmc.* APIs used for ddms support.
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
new file mode 100644
index 0000000..962bd7f
--- /dev/null
+++ b/test/098-ddmc/src/Main.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        String name = System.getProperty("java.vm.name");
+        if (!"Dalvik".equals(name)) {
+            System.out.println("This test is not supported on " + name);
+            return;
+        }
+        testRecentAllocationTracking();
+    }
+
+    private static void testRecentAllocationTracking() throws Exception {
+        System.out.println("Confirm empty");
+        Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("empty=" + empty);
+
+        System.out.println("Confirm enable");
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+        DdmVmInternal.enableRecentAllocations(true);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+        System.out.println("Capture some allocations (note just this causes allocations)");
+        Allocations before = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("before > 0=" + (before.numberOfEntries > 0));
+
+        System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
+        final int overflowAllocations = 64 * 1024;  // Won't fit in unsigned 16-bit value.
+        for (int i = 0; i < overflowAllocations; i++) {
+            new String("fnord");
+        }
+        Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
+        System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
+        System.out.println("after.numberOfEntries=" + after.numberOfEntries);
+
+        System.out.println("Disable and confirm back to empty");
+        DdmVmInternal.enableRecentAllocations(false);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+        Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("reset=" + reset);
+
+        System.out.println("Confirm we can disable twice in a row");
+        DdmVmInternal.enableRecentAllocations(false);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+        DdmVmInternal.enableRecentAllocations(false);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+        System.out.println("Confirm we can reenable twice in a row without losing allocations");
+        DdmVmInternal.enableRecentAllocations(true);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+        for (int i = 0; i < 16 * 1024; i++) {
+            new String("fnord");
+        }
+        Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
+        DdmVmInternal.enableRecentAllocations(true);
+        System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+        Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
+
+        System.out.println("Goodbye");
+        DdmVmInternal.enableRecentAllocations(false);
+        Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
+        System.out.println("goodbye=" + goodbye);
+    }
+
+    private static class Allocations {
+        final int messageHeaderLen;
+        final int entryHeaderLen;
+        final int stackFrameLen;
+        final int numberOfEntries;
+        final int offsetToStringTableFromStartOfMessage;
+        final int numberOfClassNameStrings;
+        final int numberOfMethodNameStrings;
+        final int numberOfSourceFileNameStrings;
+
+        Allocations(byte[] allocations) {
+            ByteBuffer b = ByteBuffer.wrap(allocations);
+            messageHeaderLen = b.get() & 0xff;
+            if (messageHeaderLen != 15) {
+                throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
+            }
+            entryHeaderLen = b.get() & 0xff;
+            if (entryHeaderLen != 9) {
+                throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
+            }
+            stackFrameLen = b.get() & 0xff;
+            if (stackFrameLen != 8) {
+                throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
+            }
+            numberOfEntries = b.getShort() & 0xffff;
+            offsetToStringTableFromStartOfMessage = b.getInt();
+            numberOfClassNameStrings = b.getShort() & 0xffff;
+            numberOfMethodNameStrings = b.getShort() & 0xffff;
+            numberOfSourceFileNameStrings = b.getShort() & 0xffff;
+        }
+
+        public String toString() {
+            return ("Allocations[message header len: " + messageHeaderLen +
+                    " entry header len: " + entryHeaderLen +
+                    " stack frame len: " + stackFrameLen +
+                    " number of entries: " + numberOfEntries +
+                    " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
+                    " number of class name strings: " + numberOfClassNameStrings +
+                    " number of method name strings: " + numberOfMethodNameStrings +
+                    " number of source file name strings: " + numberOfSourceFileNameStrings +
+                    "]");
+        }
+    }
+
+    private static class DdmVmInternal {
+        private static final Method enableRecentAllocationsMethod;
+        private static final Method getRecentAllocationStatusMethod;
+        private static final Method getRecentAllocationsMethod;
+        static {
+            try {
+                Class c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
+                enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
+                                                                    Boolean.TYPE);
+                getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
+                getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public static void enableRecentAllocations(boolean enable) throws Exception {
+            enableRecentAllocationsMethod.invoke(null, enable);
+        }
+        public static boolean getRecentAllocationStatus() throws Exception {
+            return (boolean) getRecentAllocationStatusMethod.invoke(null);
+        }
+        public static byte[] getRecentAllocations() throws Exception {
+            return (byte[]) getRecentAllocationsMethod.invoke(null);
+        }
+    }
+}
diff --git a/test/120-hashcode/expected.txt b/test/120-hashcode/expected.txt
new file mode 100644
index 0000000..619c561
--- /dev/null
+++ b/test/120-hashcode/expected.txt
@@ -0,0 +1 @@
+Done.
diff --git a/test/120-hashcode/info.txt b/test/120-hashcode/info.txt
new file mode 100644
index 0000000..80f131d
--- /dev/null
+++ b/test/120-hashcode/info.txt
@@ -0,0 +1 @@
+Check that object hashCode and System.identityHashCode never cause the hash to change.
\ No newline at end of file
diff --git a/test/120-hashcode/src/Main.java b/test/120-hashcode/src/Main.java
new file mode 100644
index 0000000..d2435ce
--- /dev/null
+++ b/test/120-hashcode/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * 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) {
+        Object o = new Object();
+        // Generate a hashcode and put it in the lock word.
+        int hashOrig = o.hashCode();
+        int hashInflated = 0;
+        int hashSystemOrig = System.identityHashCode(o);
+        int hashSystemInflated = 0;
+        // Inflate the monitor to move the hash from the lock word to the Monitor.
+        synchronized (o) {
+            hashInflated = o.hashCode();
+            hashSystemInflated = System.identityHashCode(o);
+        }
+        // Make sure that all the hashes agree.
+        if (hashOrig != hashInflated || hashOrig != hashSystemOrig ||
+            hashSystemOrig != hashSystemInflated) {
+            System.err.println("hash codes dont match: " + hashOrig + " " + hashInflated + " " +
+            hashSystemOrig + " " + hashSystemInflated);
+        }
+        System.out.println("Done.");
+    }
+}
+
+
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
index 5d27e6d..b5e95b0 100644
--- a/test/407-arrays/src/Main.java
+++ b/test/407-arrays/src/Main.java
@@ -57,7 +57,7 @@
                               int[] ints, Object[] objects, long[] longs, int index) {
     bools[0] = true;
     assertEquals(true, bools[0]);
-    bools[1] = true;
+    bools[index] = true;
     assertEquals(true, bools[index]);
 
     bytes[0] = -4;
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 4485590..49aa912 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -182,7 +182,9 @@
   # since we are relocating. Get the total size of the /system/framework directory
   # in 512 byte blocks and set it as the ulimit. This should be more than enough
   # room.
-  ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+  if [ ! `uname` = "Darwin" ]; then  # TODO: Darwin doesn't support "du -B..."
+    ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+  fi
 else
   FLAGS="${FLAGS} -Xnorelocate"
   COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
diff --git a/test/run-test b/test/run-test
index eed28a5..b140fbf 100755
--- a/test/run-test
+++ b/test/run-test
@@ -223,10 +223,10 @@
 #
 # 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.
+# Cannot use readlink -m, as it does not exist on Mac.
+# Fallback to nuclear option:
 noncanonical_tmp_dir=$tmp_dir
-tmp_dir="`cd $oldwd ; readlink -m $tmp_dir`"
-
+tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`"
 mkdir -p $tmp_dir
 
 if [ "$basic_verify" = "true" ]; then
diff --git a/tools/Android.mk b/tools/Android.mk
index d3be17f..9a96f7a 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -27,3 +27,13 @@
 	@echo "Copy: $(PRIVATE_MODULE) ($@)"
 	$(copy-file-to-new-target)
 	$(hide) chmod 755 $@
+
+# Copy the art shell script to the target's bin directory
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := art
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
diff --git a/tools/art b/tools/art
index 85517d3..1afcc38 100644
--- a/tools/art
+++ b/tools/art
@@ -14,27 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-lib=-XXlib:libart.so
-invoke_with=
-
-while true; do
-  if [ "$1" = "--invoke-with" ]; then
-    shift
-    invoke_with="$1"
-    shift
-  elif [ "$1" = "-d" ]; then
-    lib="-XXlib:libartd.so"
-    shift
-  elif expr "$1" : "--" >/dev/null 2>&1; then
-    echo "unknown option: $1" 1>&2
-    exit 1
-  else
-    break
-  fi
-done
-
 function follow_links() {
-  file="$1"
+  if [ z"$BASH_SOURCE" != z ]; then
+    file="$BASH_SOURCE"
+  else
+    file="$0"
+  fi
   while [ -h "$file" ]; do
     # On Mac OS, readlink -f doesn't work.
     file="$(readlink "$file")"
@@ -42,13 +27,6 @@
   echo "$file"
 }
 
-PROG_NAME="$(follow_links "$BASH_SOURCE")"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/"
-ANDROID_HOST_OUT=$PROG_DIR/..
-ANDROID_DATA=$PWD/android-data$$
-DALVIKVM_EXECUTABLE=$ANDROID_HOST_OUT/bin/dalvikvm
-
 function find_libdir() {
   if [ "$(readlink "$DALVIKVM_EXECUTABLE")" = "dalvikvm64" ]; then
     echo "lib64"
@@ -57,14 +35,39 @@
   fi
 }
 
-LD_LIBRARY_PATH=$ANDROID_HOST_OUT/"$(find_libdir)"
+PROG_NAME="$(follow_links)"
+PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+ANDROID_ROOT=$PROG_DIR/..
+ANDROID_DATA=$PWD/android-data$$
+DALVIKVM_EXECUTABLE=$ANDROID_ROOT/bin/dalvikvm
+LIBDIR=$(find_libdir)
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
 
-mkdir -p $ANDROID_DATA/dalvik-cache/{x86,x86_64}
+lib=-XXlib:$ANDROID_ROOT/$LIBDIR/libart.so
+invoke_with=
+
+while true; do
+  if [ "$1" = "--invoke-with" ]; then
+    shift
+    invoke_with="$1"
+    shift
+  elif [ "$1" = "-d" ]; then
+    lib="-XXlib:$LIBDIR/libartd.so"
+    shift
+  elif expr "$1" : "--" >/dev/null 2>&1; then
+    echo "unknown option: $1" 1>&2
+    exit 1
+  else
+    break
+  fi
+done
+
+mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
 ANDROID_DATA=$ANDROID_DATA \
-  ANDROID_ROOT=$ANDROID_HOST_OUT \
+  ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
   $invoke_with $DALVIKVM_EXECUTABLE $lib \
-    -Ximage:$ANDROID_HOST_OUT/framework/core.art \
+    -Ximage:$ANDROID_ROOT/framework/core.art \
      "$@"
 EXIT_STATUS=$?
 rm -rf $ANDROID_DATA
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
new file mode 100755
index 0000000..b66191f
--- /dev/null
+++ b/tools/symbolize.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# 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.
+#
+#
+# Symbolize oat files from the dalvik cache of a device.
+#
+# By default, pulls everything from the dalvik cache. A simple yes/no/quit prompt for each file can
+# be requested by giving "--interactive" as a parameter.
+
+INTERACTIVE="no"
+if [ "x$1" = "x--interactive" ] ; then
+  INTERACTIVE="yes"
+fi
+
+# Pull the file from the device and symbolize it.
+function one() {
+  echo $1 $2
+  if [ "x$INTERACTIVE" = "xyes" ] ; then
+    echo -n "What to do? [Y/n/q] "
+    read -e input
+    if [ "x$input" = "xn" ] ; then
+      return
+    fi
+    if [ "x$input" = "xq" ] ; then
+      exit 0
+    fi
+  fi
+  adb pull /data/dalvik-cache/$1/$2 /tmp || exit 1
+  mkdir -p $OUT/symbols/data/dalvik-cache/$1
+  oatdump --symbolize=/tmp/$2 --output=$OUT/symbols/data/dalvik-cache/$1/$2
+}
+
+# adb shell ls seems to output in DOS format (CRLF), which messes up scripting
+function adbls() {
+  adb shell ls $@ | sed 's/\r$//'
+}
+
+# Check for all ISA directories on device.
+function all() {
+  DIRS=$(adbls /data/dalvik-cache/)
+  for DIR in $DIRS ; do
+    case $DIR in
+      arm|arm64|mips|x86|x86_64)
+        FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
+        for FILE in $FILES ; do
+          # Cannot use basename as the file doesn't exist.
+          NAME=$(echo $FILE | sed -e 's/.*\///')
+          one $DIR $NAME
+        done
+        ;;
+    esac
+  done
+}
+
+all